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 About ``` 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 `` 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: () => ( <>
), }) ``` The `` 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 (
    {data.results.map((person) => (
  • {person.name}
  • ))}
); }, }); ``` 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( ); } ``` You can also add TanStack Query Devtools to the root route (optional). ```tsx import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; const rootRoute = createRootRoute({ component: () => ( <> ), }); ``` 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 (
    {data.map((person) => (
  • {person.name}
  • ))}
); } 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 (
); } 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 (
Doubled - {doubledCount}
); } 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 (
Better Auth UI Logo BETTER-AUTH. STARTER
) } ================================================ 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 ( e.preventDefault()} > setTheme("light")}> Light setTheme("dark")}> Dark setTheme("system")}> System ) } ================================================ 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 ( navigate({ href })} replace={(href) => navigate({ href, replace: true })} Link={({ href, ...props }) => } > {children} ) } ================================================ 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 & { asChild?: boolean }) { const Comp = asChild ? Slot : "button" return ( ) } 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) { return } function DropdownMenuPortal({ ...props }: React.ComponentProps) { return ( ) } function DropdownMenuTrigger({ ...props }: React.ComponentProps) { return ( ) } function DropdownMenuContent({ className, sideOffset = 4, ...props }: React.ComponentProps) { return ( ) } function DropdownMenuGroup({ ...props }: React.ComponentProps) { return ( ) } function DropdownMenuItem({ className, inset, variant = "default", ...props }: React.ComponentProps & { inset?: boolean variant?: "default" | "destructive" }) { return ( ) } function DropdownMenuCheckboxItem({ className, children, checked, ...props }: React.ComponentProps) { return ( {children} ) } function DropdownMenuRadioGroup({ ...props }: React.ComponentProps) { return ( ) } function DropdownMenuRadioItem({ className, children, ...props }: React.ComponentProps) { return ( {children} ) } function DropdownMenuLabel({ className, inset, ...props }: React.ComponentProps & { inset?: boolean }) { return ( ) } function DropdownMenuSeparator({ className, ...props }: React.ComponentProps) { return ( ) } function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<"span">) { return ( ) } function DropdownMenuSub({ ...props }: React.ComponentProps) { return ( ) } function DropdownMenuSubTrigger({ className, inset, children, ...props }: React.ComponentProps & { inset?: boolean }) { return ( {children} ) } function DropdownMenuSubContent({ className, ...props }: React.ComponentProps) { return ( ) } 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() import type { getRouter } from './router.tsx' import type { createStart } from '@tanstack/react-start' declare module '@tanstack/react-start' { interface Register { ssr: true router: Awaited> } } ================================================ 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 (
{children} } ]} /> ) } ================================================ 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 (
) } ================================================ 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 (
) } ================================================ FILE: src/routes/index.tsx ================================================ import { createFileRoute } from "@tanstack/react-router" export const Route = createFileRoute("/")({ component: IndexPage }) function IndexPage() { return (

Hello, world.

) } ================================================ 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