Repository: daveyplate/better-auth-tanstack-starter
Branch: main
Commit: 419b44fe76f9
Files: 30
Total size: 44.3 KB
Directory structure:
gitextract_dl_9g3k4/
├── .gitignore
├── README.md
├── auth-schema.ts
├── biome.json
├── components.json
├── package.json
├── public/
│ ├── manifest.json
│ └── robots.txt
├── src/
│ ├── components/
│ │ ├── header.tsx
│ │ ├── meta-theme.ts
│ │ ├── mode-toggle.tsx
│ │ ├── providers.tsx
│ │ └── ui/
│ │ ├── button.tsx
│ │ └── dropdown-menu.tsx
│ ├── database/
│ │ ├── db.ts
│ │ └── schema.ts
│ ├── lib/
│ │ ├── auth-client.ts
│ │ ├── auth.ts
│ │ └── utils.ts
│ ├── routeTree.gen.ts
│ ├── router.tsx
│ ├── routes/
│ │ ├── __root.tsx
│ │ ├── account/
│ │ │ └── $path.tsx
│ │ ├── api/
│ │ │ └── auth/
│ │ │ └── $.ts
│ │ ├── auth/
│ │ │ └── $path.tsx
│ │ └── index.tsx
│ └── styles/
│ ├── custom.css
│ └── styles.css
├── tsconfig.json
└── vite.config.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
node_modules
.DS_Store
dist
dist-ssr
*.local
count.txt
.env
.nitro
.tanstack
.wrangler
.output
.vinxi
todos.json
.vscode
.vercel
================================================
FILE: README.md
================================================
Welcome to your new TanStack app!
# Getting Started
To run this application:
```bash
pnpm install
pnpm start
```
# Building For Production
To build this application for production:
```bash
pnpm build
```
## Testing
This project uses [Vitest](https://vitest.dev/) for testing. You can run the tests with:
```bash
pnpm test
```
## Styling
This project uses [Tailwind CSS](https://tailwindcss.com/) for styling.
## Linting & Formatting
This project uses [Biome](https://biomejs.dev/) for linting and formatting. The following scripts are available:
```bash
pnpm lint
pnpm format
pnpm check
```
## Routing
This project uses [TanStack Router](https://tanstack.com/router). The initial setup is a file based router. Which means that the routes are managed as files in `src/routes`.
### Adding A Route
To add a new route to your application just add another a new file in the `./src/routes` directory.
TanStack will automatically generate the content of the route file for you.
Now that you have two routes you can use a `Link` component to navigate between them.
### Adding Links
To use SPA (Single Page Application) navigation you will need to import the `Link` component from `@tanstack/react-router`.
```tsx
import { Link } from "@tanstack/react-router";
```
Then anywhere in your JSX you can use it like so:
```tsx
<Link to="/about">About</Link>
```
This will create a link that will navigate to the `/about` route.
More information on the `Link` component can be found in the [Link documentation](https://tanstack.com/router/v1/docs/framework/react/api/router/linkComponent).
### Using A Layout
In the File Based Routing setup the layout is located in `src/routes/__root.tsx`. Anything you add to the root route will appear in all the routes. The route content will appear in the JSX where you use the `<Outlet />` component.
Here is an example layout that includes a header:
```tsx
import { Outlet, createRootRoute } from '@tanstack/react-router'
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
import { Link } from "@tanstack/react-router";
export const Route = createRootRoute({
component: () => (
<>
<header>
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
</nav>
</header>
<Outlet />
<TanStackRouterDevtools />
</>
),
})
```
The `<TanStackRouterDevtools />` component is not required so you can remove it if you don't want it in your layout.
More information on layouts can be found in the [Layouts documentation](https://tanstack.com/router/latest/docs/framework/react/guide/routing-concepts#layouts).
## Data Fetching
There are multiple ways to fetch data in your application. You can use TanStack Query to fetch data from a server. But you can also use the `loader` functionality built into TanStack Router to load the data for a route before it's rendered.
For example:
```tsx
const peopleRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/people",
loader: async () => {
const response = await fetch("https://swapi.dev/api/people");
return response.json() as Promise<{
results: {
name: string;
}[];
}>;
},
component: () => {
const data = peopleRoute.useLoaderData();
return (
<ul>
{data.results.map((person) => (
<li key={person.name}>{person.name}</li>
))}
</ul>
);
},
});
```
Loaders simplify your data fetching logic dramatically. Check out more information in the [Loader documentation](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#loader-parameters).
### React-Query
React-Query is an excellent addition or alternative to route loading and integrating it into you application is a breeze.
First add your dependencies:
```bash
pnpm add @tanstack/react-query @tanstack/react-query-devtools
```
Next we'll need to create a query client and provider. We recommend putting those in `main.tsx`.
```tsx
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
// ...
const queryClient = new QueryClient();
// ...
if (!rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement);
root.render(
<QueryClientProvider client={queryClient}>
<RouterProvider router={router} />
</QueryClientProvider>
);
}
```
You can also add TanStack Query Devtools to the root route (optional).
```tsx
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
const rootRoute = createRootRoute({
component: () => (
<>
<Outlet />
<ReactQueryDevtools buttonPosition="top-right" />
<TanStackRouterDevtools />
</>
),
});
```
Now you can use `useQuery` to fetch your data.
```tsx
import { useQuery } from "@tanstack/react-query";
import "./App.css";
function App() {
const { data } = useQuery({
queryKey: ["people"],
queryFn: () =>
fetch("https://swapi.dev/api/people")
.then((res) => res.json())
.then((data) => data.results as { name: string }[]),
initialData: [],
});
return (
<div>
<ul>
{data.map((person) => (
<li key={person.name}>{person.name}</li>
))}
</ul>
</div>
);
}
export default App;
```
You can find out everything you need to know on how to use React-Query in the [React-Query documentation](https://tanstack.com/query/latest/docs/framework/react/overview).
## State Management
Another common requirement for React applications is state management. There are many options for state management in React. TanStack Store provides a great starting point for your project.
First you need to add TanStack Store as a dependency:
```bash
pnpm add @tanstack/store
```
Now let's create a simple counter in the `src/App.tsx` file as a demonstration.
```tsx
import { useStore } from "@tanstack/react-store";
import { Store } from "@tanstack/store";
import "./App.css";
const countStore = new Store(0);
function App() {
const count = useStore(countStore);
return (
<div>
<button onClick={() => countStore.setState((n) => n + 1)}>
Increment - {count}
</button>
</div>
);
}
export default App;
```
One of the many nice features of TanStack Store is the ability to derive state from other state. That derived state will update when the base state updates.
Let's check this out by doubling the count using derived state.
```tsx
import { useStore } from "@tanstack/react-store";
import { Store, Derived } from "@tanstack/store";
import "./App.css";
const countStore = new Store(0);
const doubledStore = new Derived({
fn: () => countStore.state * 2,
deps: [countStore],
});
doubledStore.mount();
function App() {
const count = useStore(countStore);
const doubledCount = useStore(doubledStore);
return (
<div>
<button onClick={() => countStore.setState((n) => n + 1)}>
Increment - {count}
</button>
<div>Doubled - {doubledCount}</div>
</div>
);
}
export default App;
```
We use the `Derived` class to create a new store that is derived from another store. The `Derived` class has a `mount` method that will start the derived store updating.
Once we've created the derived store we can use it in the `App` component just like we would any other store using the `useStore` hook.
You can find out everything you need to know on how to use TanStack Store in the [TanStack Store documentation](https://tanstack.com/store/latest).
# Demo files
Files prefixed with `demo` can be safely deleted. They are there to provide a starting point for you to play around with the features you've installed.
# Learn More
You can learn more about all of the offerings from TanStack in the [TanStack documentation](https://tanstack.com).
================================================
FILE: auth-schema.ts
================================================
import { boolean, pgTable, text, timestamp } from "drizzle-orm/pg-core"
export const users = pgTable("users", {
id: text("id").primaryKey(),
name: text("name").notNull(),
email: text("email").notNull().unique(),
emailVerified: boolean("email_verified").notNull(),
image: text("image"),
createdAt: timestamp("created_at").notNull(),
updatedAt: timestamp("updated_at").notNull()
})
export const sessions = pgTable("sessions", {
id: text("id").primaryKey(),
expiresAt: timestamp("expires_at").notNull(),
token: text("token").notNull().unique(),
createdAt: timestamp("created_at").notNull(),
updatedAt: timestamp("updated_at").notNull(),
ipAddress: text("ip_address"),
userAgent: text("user_agent"),
userId: text("user_id")
.notNull()
.references(() => users.id, { onDelete: "cascade" })
})
export const accounts = pgTable("accounts", {
id: text("id").primaryKey(),
accountId: text("account_id").notNull(),
providerId: text("provider_id").notNull(),
userId: text("user_id")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
accessToken: text("access_token"),
refreshToken: text("refresh_token"),
idToken: text("id_token"),
accessTokenExpiresAt: timestamp("access_token_expires_at"),
refreshTokenExpiresAt: timestamp("refresh_token_expires_at"),
scope: text("scope"),
password: text("password"),
createdAt: timestamp("created_at").notNull(),
updatedAt: timestamp("updated_at").notNull()
})
export const verifications = pgTable("verifications", {
id: text("id").primaryKey(),
identifier: text("identifier").notNull(),
value: text("value").notNull(),
expiresAt: timestamp("expires_at").notNull(),
createdAt: timestamp("created_at"),
updatedAt: timestamp("updated_at")
})
================================================
FILE: biome.json
================================================
{
"$schema": "https://biomejs.dev/schemas/2.2.7/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
},
"files": {
"ignoreUnknown": true,
"includes": ["**", "!**/src/routeTree.gen.ts", "!**/*.css"]
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 4
},
"assist": { "actions": { "source": { "organizeImports": "on" } } },
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"style": {
"noNonNullAssertion": "off"
}
}
},
"javascript": {
"formatter": {
"quoteStyle": "double",
"semicolons": "asNeeded",
"trailingCommas": "none"
}
}
}
================================================
FILE: components.json
================================================
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/styles.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}
================================================
FILE: package.json
================================================
{
"name": "tanstack-start-hybrid",
"private": true,
"type": "module",
"scripts": {
"dev": "vite dev --port 3000",
"build": "vite build",
"serve": "vite preview",
"test": "vitest run",
"format": "biome format",
"lint": "biome lint",
"check": "biome check"
},
"dependencies": {
"@daveyplate/better-auth-ui": "^3.3.0",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-slot": "^1.2.4",
"@tailwindcss/vite": "^4.1.18",
"@tanstack/nitro-v2-vite-plugin": "^1.141.0",
"@tanstack/react-devtools": "^0.8.4",
"@tanstack/react-router": "^1.141.2",
"@tanstack/react-router-devtools": "^1.141.2",
"@tanstack/react-router-ssr-query": "^1.141.2",
"@tanstack/react-start": "^1.141.3",
"@tanstack/router-plugin": "^1.141.2",
"better-auth": "^1.4.7",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"dotenv": "^17.2.3",
"drizzle-orm": "^0.45.1",
"lucide-react": "^0.561.0",
"next-themes": "^0.4.6",
"pg": "^8.16.3",
"react": "^19.2.3",
"react-dom": "^19.2.3",
"tailwind-merge": "^3.4.0",
"tailwindcss": "^4.1.18",
"tailwindcss-safe-area": "^1.2.0",
"vite-tsconfig-paths": "^6.0.1"
},
"devDependencies": {
"@biomejs/biome": "2.3.8",
"@testing-library/dom": "^10.4.1",
"@testing-library/react": "^16.3.0",
"@types/node": "^25.0.2",
"@types/pg": "^8.16.0",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.2",
"drizzle-kit": "^0.31.8",
"jsdom": "^27.3.0",
"tsx": "^4.21.0",
"tw-animate-css": "^1.4.0",
"typescript": "^5.9.3",
"vite": "^7.2.7",
"vite-plugin-devtools-json": "^1.0.0",
"vitest": "^4.0.15",
"web-vitals": "^5.1.0"
}
}
================================================
FILE: public/manifest.json
================================================
{
"short_name": "TanStack App",
"name": "Create TanStack App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
================================================
FILE: public/robots.txt
================================================
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
================================================
FILE: src/components/header.tsx
================================================
import { GitHubIcon, UserButton } from "@daveyplate/better-auth-ui"
import { Link } from "@tanstack/react-router"
import { ModeToggle } from "./mode-toggle"
import { Button } from "./ui/button"
export function Header() {
return (
<header className="sticky top-0 z-50 flex h-12 justify-between border-b bg-background/60 px-safe-or-4 backdrop-blur md:h-14 md:px-safe-or-6">
<Link to="/" className="flex items-center gap-2">
<svg
aria-label="Better-Auth Logo"
className="size-5"
fill="none"
height="45"
viewBox="0 0 60 45"
width="60"
xmlns="http://www.w3.org/2000/svg"
>
<title>Better Auth UI Logo</title>
<path
className="fill-black dark:fill-white"
clipRule="evenodd"
d="M0 0H15V45H0V0ZM45 0H60V45H45V0ZM20 0H40V15H20V0ZM20 30H40V45H20V30Z"
fillRule="evenodd"
/>
</svg>
BETTER-AUTH. STARTER
</Link>
<div className="flex items-center gap-2">
<a
href="https://github.com/daveyplate/better-auth-nextjs-starter"
target="_blank"
rel="noopener"
>
<Button
variant="outline"
size="icon"
className="size-8 rounded-full"
>
<GitHubIcon />
</Button>
</a>
<ModeToggle />
<UserButton size="icon" />
</div>
</header>
)
}
================================================
FILE: src/components/meta-theme.ts
================================================
import { useEffect } from "react"
export function MetaTheme() {
useEffect(() => {
const updateThemeColor = () => {
const bgColor = window.getComputedStyle(
document.body
).backgroundColor
const metaThemeColor = document.querySelector(
"meta[name=theme-color]"
)
metaThemeColor?.setAttribute("content", bgColor)
}
const observer = new MutationObserver(updateThemeColor)
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ["class"]
})
return () => observer.disconnect()
}, [])
return null
}
================================================
FILE: src/components/mode-toggle.tsx
================================================
"use client"
import { MonitorIcon, MoonIcon, SunIcon } from "lucide-react"
import { useTheme } from "next-themes"
import { Button } from "./ui/button"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger
} from "./ui/dropdown-menu"
export function ModeToggle() {
const { setTheme } = useTheme()
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
size="icon"
className="size-8 rounded-full"
>
<SunIcon className="dark:-rotate-90 h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:scale-0" />
<MoonIcon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span className="sr-only">Toggle theme</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="end"
onCloseAutoFocus={(e) => e.preventDefault()}
>
<DropdownMenuItem onClick={() => setTheme("light")}>
<SunIcon />
Light
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("dark")}>
<MoonIcon />
Dark
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("system")}>
<MonitorIcon />
System
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
}
================================================
FILE: src/components/providers.tsx
================================================
import { AuthUIProvider } from "@daveyplate/better-auth-ui"
import { Link, useRouter } from "@tanstack/react-router"
import { ThemeProvider } from "next-themes"
import { authClient } from "@/lib/auth-client"
import { MetaTheme } from "./meta-theme"
export function Providers({ children }: { children: React.ReactNode }) {
const { navigate } = useRouter()
return (
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
<AuthUIProvider
authClient={authClient}
navigate={(href) => navigate({ href })}
replace={(href) => navigate({ href, replace: true })}
Link={({ href, ...props }) => <Link to={href} {...props} />}
>
{children}
<MetaTheme />
</AuthUIProvider>
</ThemeProvider>
)
}
================================================
FILE: src/components/ui/button.tsx
================================================
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import type * as React from "react"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
outline:
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
link: "text-primary underline-offset-4 hover:underline"
},
size: {
default: "h-9 px-4 py-2 has-[>svg]:px-3",
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
icon: "size-9",
"icon-sm": "size-8",
"icon-lg": "size-10"
}
},
defaultVariants: {
variant: "default",
size: "default"
}
}
)
function Button({
className,
variant,
size,
asChild = false,
...props
}: React.ComponentProps<"button"> &
VariantProps<typeof buttonVariants> & {
asChild?: boolean
}) {
const Comp = asChild ? Slot : "button"
return (
<Comp
data-slot="button"
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
)
}
export { Button, buttonVariants }
================================================
FILE: src/components/ui/dropdown-menu.tsx
================================================
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
import type * as React from "react"
import { cn } from "@/lib/utils"
function DropdownMenu({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />
}
function DropdownMenuPortal({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
return (
<DropdownMenuPrimitive.Portal
data-slot="dropdown-menu-portal"
{...props}
/>
)
}
function DropdownMenuTrigger({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
return (
<DropdownMenuPrimitive.Trigger
data-slot="dropdown-menu-trigger"
{...props}
/>
)
}
function DropdownMenuContent({
className,
sideOffset = 4,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
return (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content
data-slot="dropdown-menu-content"
sideOffset={sideOffset}
className={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
className
)}
{...props}
/>
</DropdownMenuPrimitive.Portal>
)
}
function DropdownMenuGroup({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
return (
<DropdownMenuPrimitive.Group
data-slot="dropdown-menu-group"
{...props}
/>
)
}
function DropdownMenuItem({
className,
inset,
variant = "default",
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean
variant?: "default" | "destructive"
}) {
return (
<DropdownMenuPrimitive.Item
data-slot="dropdown-menu-item"
data-inset={inset}
data-variant={variant}
className={cn(
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
/>
)
}
function DropdownMenuCheckboxItem({
className,
children,
checked,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
return (
<DropdownMenuPrimitive.CheckboxItem
data-slot="dropdown-menu-checkbox-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
checked={checked}
{...props}
>
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<CheckIcon className="size-4" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.CheckboxItem>
)
}
function DropdownMenuRadioGroup({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
return (
<DropdownMenuPrimitive.RadioGroup
data-slot="dropdown-menu-radio-group"
{...props}
/>
)
}
function DropdownMenuRadioItem({
className,
children,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
return (
<DropdownMenuPrimitive.RadioItem
data-slot="dropdown-menu-radio-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
>
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<CircleIcon className="size-2 fill-current" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.RadioItem>
)
}
function DropdownMenuLabel({
className,
inset,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
inset?: boolean
}) {
return (
<DropdownMenuPrimitive.Label
data-slot="dropdown-menu-label"
data-inset={inset}
className={cn(
"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
className
)}
{...props}
/>
)
}
function DropdownMenuSeparator({
className,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
return (
<DropdownMenuPrimitive.Separator
data-slot="dropdown-menu-separator"
className={cn("bg-border -mx-1 my-1 h-px", className)}
{...props}
/>
)
}
function DropdownMenuShortcut({
className,
...props
}: React.ComponentProps<"span">) {
return (
<span
data-slot="dropdown-menu-shortcut"
className={cn(
"text-muted-foreground ml-auto text-xs tracking-widest",
className
)}
{...props}
/>
)
}
function DropdownMenuSub({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
return (
<DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />
)
}
function DropdownMenuSubTrigger({
className,
inset,
children,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
inset?: boolean
}) {
return (
<DropdownMenuPrimitive.SubTrigger
data-slot="dropdown-menu-sub-trigger"
data-inset={inset}
className={cn(
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
>
{children}
<ChevronRightIcon className="ml-auto size-4" />
</DropdownMenuPrimitive.SubTrigger>
)
}
function DropdownMenuSubContent({
className,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
return (
<DropdownMenuPrimitive.SubContent
data-slot="dropdown-menu-sub-content"
className={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
className
)}
{...props}
/>
)
}
export {
DropdownMenu,
DropdownMenuPortal,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuLabel,
DropdownMenuItem,
DropdownMenuCheckboxItem,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuSub,
DropdownMenuSubTrigger,
DropdownMenuSubContent
}
================================================
FILE: src/database/db.ts
================================================
import { drizzle } from "drizzle-orm/node-postgres"
export const db = drizzle(process.env.DATABASE_URL!)
================================================
FILE: src/database/schema.ts
================================================
export * from "@/../auth-schema"
================================================
FILE: src/lib/auth-client.ts
================================================
import { createAuthClient } from "better-auth/react"
export const authClient = createAuthClient({})
================================================
FILE: src/lib/auth.ts
================================================
import { betterAuth } from "better-auth"
import { drizzleAdapter } from "better-auth/adapters/drizzle"
import { db } from "@/database/db"
import * as schema from "@/database/schema"
export const auth = betterAuth({
database: drizzleAdapter(db, {
provider: "pg",
usePlural: true,
schema
}),
emailAndPassword: {
enabled: true
}
})
================================================
FILE: src/lib/utils.ts
================================================
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
================================================
FILE: src/routeTree.gen.ts
================================================
/* eslint-disable */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// This file was automatically generated by TanStack Router.
// You should NOT make any changes in this file as it will be overwritten.
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
import { Route as rootRouteImport } from './routes/__root'
import { Route as IndexRouteImport } from './routes/index'
import { Route as AuthPathRouteImport } from './routes/auth/$path'
import { Route as AccountPathRouteImport } from './routes/account/$path'
import { Route as ApiAuthSplatRouteImport } from './routes/api/auth/$'
const IndexRoute = IndexRouteImport.update({
id: '/',
path: '/',
getParentRoute: () => rootRouteImport,
} as any)
const AuthPathRoute = AuthPathRouteImport.update({
id: '/auth/$path',
path: '/auth/$path',
getParentRoute: () => rootRouteImport,
} as any)
const AccountPathRoute = AccountPathRouteImport.update({
id: '/account/$path',
path: '/account/$path',
getParentRoute: () => rootRouteImport,
} as any)
const ApiAuthSplatRoute = ApiAuthSplatRouteImport.update({
id: '/api/auth/$',
path: '/api/auth/$',
getParentRoute: () => rootRouteImport,
} as any)
export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/account/$path': typeof AccountPathRoute
'/auth/$path': typeof AuthPathRoute
'/api/auth/$': typeof ApiAuthSplatRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/account/$path': typeof AccountPathRoute
'/auth/$path': typeof AuthPathRoute
'/api/auth/$': typeof ApiAuthSplatRoute
}
export interface FileRoutesById {
__root__: typeof rootRouteImport
'/': typeof IndexRoute
'/account/$path': typeof AccountPathRoute
'/auth/$path': typeof AuthPathRoute
'/api/auth/$': typeof ApiAuthSplatRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/' | '/account/$path' | '/auth/$path' | '/api/auth/$'
fileRoutesByTo: FileRoutesByTo
to: '/' | '/account/$path' | '/auth/$path' | '/api/auth/$'
id: '__root__' | '/' | '/account/$path' | '/auth/$path' | '/api/auth/$'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
AccountPathRoute: typeof AccountPathRoute
AuthPathRoute: typeof AuthPathRoute
ApiAuthSplatRoute: typeof ApiAuthSplatRoute
}
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/': {
id: '/'
path: '/'
fullPath: '/'
preLoaderRoute: typeof IndexRouteImport
parentRoute: typeof rootRouteImport
}
'/auth/$path': {
id: '/auth/$path'
path: '/auth/$path'
fullPath: '/auth/$path'
preLoaderRoute: typeof AuthPathRouteImport
parentRoute: typeof rootRouteImport
}
'/account/$path': {
id: '/account/$path'
path: '/account/$path'
fullPath: '/account/$path'
preLoaderRoute: typeof AccountPathRouteImport
parentRoute: typeof rootRouteImport
}
'/api/auth/$': {
id: '/api/auth/$'
path: '/api/auth/$'
fullPath: '/api/auth/$'
preLoaderRoute: typeof ApiAuthSplatRouteImport
parentRoute: typeof rootRouteImport
}
}
}
const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
AccountPathRoute: AccountPathRoute,
AuthPathRoute: AuthPathRoute,
ApiAuthSplatRoute: ApiAuthSplatRoute,
}
export const routeTree = rootRouteImport
._addFileChildren(rootRouteChildren)
._addFileTypes<FileRouteTypes>()
import type { getRouter } from './router.tsx'
import type { createStart } from '@tanstack/react-start'
declare module '@tanstack/react-start' {
interface Register {
ssr: true
router: Awaited<ReturnType<typeof getRouter>>
}
}
================================================
FILE: src/router.tsx
================================================
import { createRouter } from "@tanstack/react-router"
// Import the generated route tree
import { routeTree } from "./routeTree.gen"
// Create a new router instance
export const getRouter = () => {
return createRouter({
routeTree,
scrollRestoration: true,
defaultPreloadStaleTime: 0
})
}
================================================
FILE: src/routes/__root.tsx
================================================
import { TanStackDevtools } from "@tanstack/react-devtools"
import { createRootRoute, HeadContent, Scripts } from "@tanstack/react-router"
import { TanStackRouterDevtoolsPanel } from "@tanstack/react-router-devtools"
import { Header } from "@/components/header"
import { Providers } from "@/components/providers"
import appCss from "../styles/styles.css?url"
export const Route = createRootRoute({
head: () => ({
meta: [
{ title: "Better Auth Starter" },
{ charSet: "utf-8" },
{
name: "viewport",
content: "width=device-width, initial-scale=1"
},
{
name: "theme-color",
content: "var(--bg-background)"
}
],
links: [
{
rel: "stylesheet",
href: appCss
}
]
}),
shellComponent: RootDocument
})
function RootDocument({ children }: { children: React.ReactNode }) {
return (
<html lang="en" suppressHydrationWarning>
<head>
<HeadContent />
</head>
<body className="min-h-screen flex flex-col">
<Providers>
<Header />
{children}
</Providers>
<TanStackDevtools
config={{
position: "bottom-right"
}}
plugins={[
{
name: "Tanstack Router",
render: <TanStackRouterDevtoolsPanel />
}
]}
/>
<Scripts />
</body>
</html>
)
}
================================================
FILE: src/routes/account/$path.tsx
================================================
import { AccountView } from "@daveyplate/better-auth-ui"
import { createFileRoute } from "@tanstack/react-router"
export const Route = createFileRoute("/account/$path")({
component: RouteComponent
})
function RouteComponent() {
const { path } = Route.useParams()
return (
<main className="container mx-auto p-4 md:p-6">
<AccountView
classNames={{
sidebar: {
base: "sticky top-20"
}
}}
path={path}
/>
</main>
)
}
================================================
FILE: src/routes/api/auth/$.ts
================================================
import { createFileRoute } from "@tanstack/react-router"
import { auth } from "@/lib/auth"
export const Route = createFileRoute("/api/auth/$")({
server: {
handlers: {
GET: ({ request }) => {
return auth.handler(request)
},
POST: ({ request }) => {
return auth.handler(request)
}
}
}
})
================================================
FILE: src/routes/auth/$path.tsx
================================================
import { AuthView } from "@daveyplate/better-auth-ui"
import { createFileRoute } from "@tanstack/react-router"
export const Route = createFileRoute("/auth/$path")({
component: RouteComponent
})
function RouteComponent() {
const { path } = Route.useParams()
return (
<main className="container items-center flex flex-col mx-auto my-auto p-4 md:p-6">
<AuthView path={path} />
</main>
)
}
================================================
FILE: src/routes/index.tsx
================================================
import { createFileRoute } from "@tanstack/react-router"
export const Route = createFileRoute("/")({ component: IndexPage })
function IndexPage() {
return (
<main className="container mx-auto flex flex-col gap-4 p-6">
<h1 className="text-2xl font-bold">Hello, world.</h1>
</main>
)
}
================================================
FILE: src/styles/custom.css
================================================
@import "tailwindcss-safe-area";
@import "@daveyplate/better-auth-ui/css";
@layer base {
button:not(:disabled),
[role="button"]:not(:disabled) {
cursor: pointer;
}
}
[role="menuitem"]:not(:disabled) {
cursor: pointer;
}
:root {
--warning: hsl(38 92% 50%);
--warning-foreground: hsl(48 96% 89%);
}
.dark {
--warning: hsl(48 96% 89%);
--warning-foreground: hsl(38 92% 50%);
}
@theme inline {
--color-warning: var(--warning);
--color-warning-foreground: var(--warning-foreground);
}
/** iOS Dynamic System Font Scaling */
/* @supports (-webkit-touch-callout:none) {
html {
font: -apple-system-body;
}
} */
================================================
FILE: src/styles/styles.css
================================================
@import "tailwindcss";
@import "tw-animate-css";
@import "./custom.css";
@custom-variant dark (&:is(.dark *));
@theme inline {
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
}
:root {
--radius: 0.625rem;
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
}
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.205 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.205 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.556 0 0);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.205 0 0);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.556 0 0);
}
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
}
================================================
FILE: tsconfig.json
================================================
{
"include": ["**/*.ts", "**/*.tsx"],
"compilerOptions": {
"target": "ES2022",
"jsx": "react-jsx",
"module": "ESNext",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"types": ["vite/client"],
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": false,
"noEmit": true,
/* Linting */
"skipLibCheck": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
================================================
FILE: vite.config.ts
================================================
// import { cloudflare } from "@cloudflare/vite-plugin"
import tailwindcss from "@tailwindcss/vite"
import { nitroV2Plugin } from "@tanstack/nitro-v2-vite-plugin"
import { tanstackStart } from "@tanstack/react-start/plugin/vite"
import viteReact from "@vitejs/plugin-react"
import { defineConfig } from "vite"
import devtoolsJson from "vite-plugin-devtools-json"
import viteTsConfigPaths from "vite-tsconfig-paths"
const config = defineConfig({
plugins: [
viteTsConfigPaths({
projects: ["./tsconfig.json"]
}),
tailwindcss(),
tanstackStart(),
nitroV2Plugin({ preset: "vercel" }),
viteReact(),
devtoolsJson()
]
})
export default config
gitextract_dl_9g3k4/ ├── .gitignore ├── README.md ├── auth-schema.ts ├── biome.json ├── components.json ├── package.json ├── public/ │ ├── manifest.json │ └── robots.txt ├── src/ │ ├── components/ │ │ ├── header.tsx │ │ ├── meta-theme.ts │ │ ├── mode-toggle.tsx │ │ ├── providers.tsx │ │ └── ui/ │ │ ├── button.tsx │ │ └── dropdown-menu.tsx │ ├── database/ │ │ ├── db.ts │ │ └── schema.ts │ ├── lib/ │ │ ├── auth-client.ts │ │ ├── auth.ts │ │ └── utils.ts │ ├── routeTree.gen.ts │ ├── router.tsx │ ├── routes/ │ │ ├── __root.tsx │ │ ├── account/ │ │ │ └── $path.tsx │ │ ├── api/ │ │ │ └── auth/ │ │ │ └── $.ts │ │ ├── auth/ │ │ │ └── $path.tsx │ │ └── index.tsx │ └── styles/ │ ├── custom.css │ └── styles.css ├── tsconfig.json └── vite.config.ts
SYMBOL INDEX (32 symbols across 12 files)
FILE: src/components/header.tsx
function Header (line 6) | function Header() {
FILE: src/components/meta-theme.ts
function MetaTheme (line 3) | function MetaTheme() {
FILE: src/components/mode-toggle.tsx
function ModeToggle (line 14) | function ModeToggle() {
FILE: src/components/providers.tsx
function Providers (line 8) | function Providers({ children }: { children: React.ReactNode }) {
FILE: src/components/ui/button.tsx
function Button (line 39) | function Button({
FILE: src/components/ui/dropdown-menu.tsx
function DropdownMenu (line 7) | function DropdownMenu({
function DropdownMenuPortal (line 13) | function DropdownMenuPortal({
function DropdownMenuTrigger (line 24) | function DropdownMenuTrigger({
function DropdownMenuContent (line 35) | function DropdownMenuContent({
function DropdownMenuGroup (line 55) | function DropdownMenuGroup({
function DropdownMenuItem (line 66) | function DropdownMenuItem({
function DropdownMenuCheckboxItem (line 89) | function DropdownMenuCheckboxItem({
function DropdownMenuRadioGroup (line 115) | function DropdownMenuRadioGroup({
function DropdownMenuRadioItem (line 126) | function DropdownMenuRadioItem({
function DropdownMenuLabel (line 150) | function DropdownMenuLabel({
function DropdownMenuSeparator (line 170) | function DropdownMenuSeparator({
function DropdownMenuShortcut (line 183) | function DropdownMenuShortcut({
function DropdownMenuSub (line 199) | function DropdownMenuSub({
function DropdownMenuSubTrigger (line 207) | function DropdownMenuSubTrigger({
function DropdownMenuSubContent (line 231) | function DropdownMenuSubContent({
FILE: src/lib/utils.ts
function cn (line 4) | function cn(...inputs: ClassValue[]) {
FILE: src/routeTree.gen.ts
type FileRoutesByFullPath (line 38) | interface FileRoutesByFullPath {
type FileRoutesByTo (line 44) | interface FileRoutesByTo {
type FileRoutesById (line 50) | interface FileRoutesById {
type FileRouteTypes (line 57) | interface FileRouteTypes {
type RootRouteChildren (line 65) | interface RootRouteChildren {
type FileRoutesByPath (line 73) | interface FileRoutesByPath {
type Register (line 118) | interface Register {
FILE: src/routes/__root.tsx
function RootDocument (line 33) | function RootDocument({ children }: { children: React.ReactNode }) {
FILE: src/routes/account/$path.tsx
function RouteComponent (line 8) | function RouteComponent() {
FILE: src/routes/auth/$path.tsx
function RouteComponent (line 8) | function RouteComponent() {
FILE: src/routes/index.tsx
function IndexPage (line 5) | function IndexPage() {
Condensed preview — 30 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (49K chars).
[
{
"path": ".gitignore",
"chars": 128,
"preview": "node_modules\n.DS_Store\ndist\ndist-ssr\n*.local\ncount.txt\n.env\n.nitro\n.tanstack\n.wrangler\n.output\n.vinxi\ntodos.json\n.vscode"
},
{
"path": "README.md",
"chars": 7787,
"preview": "Welcome to your new TanStack app! \n\n# Getting Started\n\nTo run this application:\n\n```bash\npnpm install\npnpm start\n```\n\n# "
},
{
"path": "auth-schema.ts",
"chars": 1851,
"preview": "import { boolean, pgTable, text, timestamp } from \"drizzle-orm/pg-core\"\n\nexport const users = pgTable(\"users\", {\n id:"
},
{
"path": "biome.json",
"chars": 834,
"preview": "{\n \"$schema\": \"https://biomejs.dev/schemas/2.2.7/schema.json\",\n \"vcs\": {\n \"enabled\": true,\n \"clientK"
},
{
"path": "components.json",
"chars": 485,
"preview": "{\n \"$schema\": \"https://ui.shadcn.com/schema.json\",\n \"style\": \"new-york\",\n \"rsc\": false,\n \"tsx\": true,\n \"t"
},
{
"path": "package.json",
"chars": 1996,
"preview": "{\n \"name\": \"tanstack-start-hybrid\",\n \"private\": true,\n \"type\": \"module\",\n \"scripts\": {\n \"dev\": \"vite "
},
{
"path": "public/manifest.json",
"chars": 592,
"preview": "{\n \"short_name\": \"TanStack App\",\n \"name\": \"Create TanStack App Sample\",\n \"icons\": [\n {\n \"src\""
},
{
"path": "public/robots.txt",
"chars": 67,
"preview": "# https://www.robotstxt.org/robotstxt.html\nUser-agent: *\nDisallow:\n"
},
{
"path": "src/components/header.tsx",
"chars": 1837,
"preview": "import { GitHubIcon, UserButton } from \"@daveyplate/better-auth-ui\"\nimport { Link } from \"@tanstack/react-router\"\nimport"
},
{
"path": "src/components/meta-theme.ts",
"chars": 701,
"preview": "import { useEffect } from \"react\"\n\nexport function MetaTheme() {\n useEffect(() => {\n const updateThemeColor = "
},
{
"path": "src/components/mode-toggle.tsx",
"chars": 1681,
"preview": "\"use client\"\n\nimport { MonitorIcon, MoonIcon, SunIcon } from \"lucide-react\"\nimport { useTheme } from \"next-themes\"\n\nimpo"
},
{
"path": "src/components/providers.tsx",
"chars": 941,
"preview": "import { AuthUIProvider } from \"@daveyplate/better-auth-ui\"\nimport { Link, useRouter } from \"@tanstack/react-router\"\nimp"
},
{
"path": "src/components/ui/button.tsx",
"chars": 2386,
"preview": "import { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport typ"
},
{
"path": "src/components/ui/dropdown-menu.tsx",
"chars": 9286,
"preview": "import * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\"\nimport { CheckIcon, ChevronRightIcon, CircleIcon "
},
{
"path": "src/database/db.ts",
"chars": 105,
"preview": "import { drizzle } from \"drizzle-orm/node-postgres\"\nexport const db = drizzle(process.env.DATABASE_URL!)\n"
},
{
"path": "src/database/schema.ts",
"chars": 33,
"preview": "export * from \"@/../auth-schema\"\n"
},
{
"path": "src/lib/auth-client.ts",
"chars": 101,
"preview": "import { createAuthClient } from \"better-auth/react\"\n\nexport const authClient = createAuthClient({})\n"
},
{
"path": "src/lib/auth.ts",
"chars": 379,
"preview": "import { betterAuth } from \"better-auth\"\nimport { drizzleAdapter } from \"better-auth/adapters/drizzle\"\n\nimport { db } fr"
},
{
"path": "src/lib/utils.ts",
"chars": 168,
"preview": "import { type ClassValue, clsx } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: Cla"
},
{
"path": "src/routeTree.gen.ts",
"chars": 3786,
"preview": "/* eslint-disable */\n\n// @ts-nocheck\n\n// noinspection JSUnusedGlobalSymbols\n\n// This file was automatically generated by"
},
{
"path": "src/router.tsx",
"chars": 322,
"preview": "import { createRouter } from \"@tanstack/react-router\"\n\n// Import the generated route tree\nimport { routeTree } from \"./r"
},
{
"path": "src/routes/__root.tsx",
"chars": 1767,
"preview": "import { TanStackDevtools } from \"@tanstack/react-devtools\"\nimport { createRootRoute, HeadContent, Scripts } from \"@tans"
},
{
"path": "src/routes/account/$path.tsx",
"chars": 583,
"preview": "import { AccountView } from \"@daveyplate/better-auth-ui\"\nimport { createFileRoute } from \"@tanstack/react-router\"\n\nexpor"
},
{
"path": "src/routes/api/auth/$.ts",
"chars": 391,
"preview": "import { createFileRoute } from \"@tanstack/react-router\"\nimport { auth } from \"@/lib/auth\"\n\nexport const Route = createF"
},
{
"path": "src/routes/auth/$path.tsx",
"chars": 433,
"preview": "import { AuthView } from \"@daveyplate/better-auth-ui\"\nimport { createFileRoute } from \"@tanstack/react-router\"\n\nexport c"
},
{
"path": "src/routes/index.tsx",
"chars": 322,
"preview": "import { createFileRoute } from \"@tanstack/react-router\"\n\nexport const Route = createFileRoute(\"/\")({ component: IndexPa"
},
{
"path": "src/styles/custom.css",
"chars": 662,
"preview": "@import \"tailwindcss-safe-area\";\n\n@import \"@daveyplate/better-auth-ui/css\";\n\n@layer base {\n button:not(:disabled),\n "
},
{
"path": "src/styles/styles.css",
"chars": 4327,
"preview": "@import \"tailwindcss\";\n@import \"tw-animate-css\";\n\n@import \"./custom.css\";\n\n@custom-variant dark (&:is(.dark *));\n\n@theme"
},
{
"path": "tsconfig.json",
"chars": 736,
"preview": "{\n \"include\": [\"**/*.ts\", \"**/*.tsx\"],\n \"compilerOptions\": {\n \"target\": \"ES2022\",\n \"jsx\": \"react-jsx"
},
{
"path": "vite.config.ts",
"chars": 712,
"preview": "// import { cloudflare } from \"@cloudflare/vite-plugin\"\nimport tailwindcss from \"@tailwindcss/vite\"\nimport { nitroV2Plug"
}
]
About this extraction
This page contains the full source code of the daveyplate/better-auth-tanstack-starter GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 30 files (44.3 KB), approximately 12.2k tokens, and a symbol index with 32 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.