From 99bd88194c7794574776e22700f43542761e85d2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gr=C3=A9goire=20Hertault?= <ghertault@gmail.com>
Date: Sat, 10 Jan 2026 14:54:55 +0100
Subject: [PATCH] examples: add tanstack start with better auth example

---
 .../start-basic-better-auth/.env.example      |   9 ++
 .../react/start-basic-better-auth/.gitignore  |   6 +
 .../react/start-basic-better-auth/README.md   |  66 +++++++++
 .../start-basic-better-auth/package.json      |  32 +++++
 .../src/components/DefaultCatchBoundary.tsx   |  53 ++++++++
 .../src/components/NotFound.tsx               |  25 ++++
 .../src/routeTree.gen.ts                      | 122 +++++++++++++++++
 .../start-basic-better-auth/src/router.tsx    |  19 +++
 .../src/routes/__root.tsx                     | 125 ++++++++++++++++++
 .../src/routes/api/auth/$.ts                  |  15 +++
 .../src/routes/index.tsx                      |  50 +++++++
 .../src/routes/login.tsx                      |  43 ++++++
 .../src/routes/protected.tsx                  |  55 ++++++++
 .../src/styles/app.css                        |   1 +
 .../src/utils/auth-client.ts                  |   3 +
 .../start-basic-better-auth/src/utils/auth.ts |  28 ++++
 .../start-basic-better-auth/tsconfig.json     |  24 ++++
 .../start-basic-better-auth/vite.config.ts    |  20 +++
 18 files changed, 696 insertions(+)
 create mode 100644 examples/react/start-basic-better-auth/.env.example
 create mode 100644 examples/react/start-basic-better-auth/.gitignore
 create mode 100644 examples/react/start-basic-better-auth/README.md
 create mode 100644 examples/react/start-basic-better-auth/package.json
 create mode 100644 examples/react/start-basic-better-auth/src/components/DefaultCatchBoundary.tsx
 create mode 100644 examples/react/start-basic-better-auth/src/components/NotFound.tsx
 create mode 100644 examples/react/start-basic-better-auth/src/routeTree.gen.ts
 create mode 100644 examples/react/start-basic-better-auth/src/router.tsx
 create mode 100644 examples/react/start-basic-better-auth/src/routes/__root.tsx
 create mode 100644 examples/react/start-basic-better-auth/src/routes/api/auth/$.ts
 create mode 100644 examples/react/start-basic-better-auth/src/routes/index.tsx
 create mode 100644 examples/react/start-basic-better-auth/src/routes/login.tsx
 create mode 100644 examples/react/start-basic-better-auth/src/routes/protected.tsx
 create mode 100644 examples/react/start-basic-better-auth/src/styles/app.css
 create mode 100644 examples/react/start-basic-better-auth/src/utils/auth-client.ts
 create mode 100644 examples/react/start-basic-better-auth/src/utils/auth.ts
 create mode 100644 examples/react/start-basic-better-auth/tsconfig.json
 create mode 100644 examples/react/start-basic-better-auth/vite.config.ts

diff --git a/examples/react/start-basic-better-auth/.env.example b/examples/react/start-basic-better-auth/.env.example
new file mode 100644
index 0000000000..6fb0f1821f
--- /dev/null
+++ b/examples/react/start-basic-better-auth/.env.example
@@ -0,0 +1,9 @@
+# Better Auth Configuration
+BETTER_AUTH_SECRET=your-secret-key-here-min-32-chars-long
+
+# Better Auth URL (base URL, no /api/auth suffix)
+BETTER_AUTH_URL=http://localhost:10000
+
+# GitHub OAuth Configuration (https://github.com/settings/developers)
+GITHUB_CLIENT_ID=your-github-client-id
+GITHUB_CLIENT_SECRET=your-github-client-secret
diff --git a/examples/react/start-basic-better-auth/.gitignore b/examples/react/start-basic-better-auth/.gitignore
new file mode 100644
index 0000000000..3479e16707
--- /dev/null
+++ b/examples/react/start-basic-better-auth/.gitignore
@@ -0,0 +1,6 @@
+node_modules
+dist
+.env
+*.local
+.DS_Store
+.tanstack
diff --git a/examples/react/start-basic-better-auth/README.md b/examples/react/start-basic-better-auth/README.md
new file mode 100644
index 0000000000..fc15980da7
--- /dev/null
+++ b/examples/react/start-basic-better-auth/README.md
@@ -0,0 +1,66 @@
+# TanStack Start - Better Auth Example
+
+A TanStack Start example demonstrating authentication with Better Auth.
+
+- [TanStack Router Docs](https://tanstack.com/router)
+- [Better Auth Documentation](https://www.better-auth.com/)
+
+## Start a new project based on this example
+
+To start a new project based on this example, run:
+
+```sh
+npx gitpick TanStack/router/tree/main/examples/react/start-basic-better-auth start-basic-better-auth
+```
+
+## Setup
+
+This example requires environment variables for Better Auth configuration. Copy the `.env.example` file to `.env` and fill in your GitHub OAuth credentials:
+
+```sh
+cp .env.example .env
+```
+
+### GitHub OAuth Setup
+
+1. Go to [GitHub Developer Settings](https://github.com/settings/developers)
+2. Click "New OAuth App"
+3. Fill in:
+   - Application name: Your app name
+   - Homepage URL: `http://localhost:10000`
+   - Authorization callback URL: `http://localhost:10000/api/auth/callback/github`
+4. Copy the Client ID and Client Secret to your `.env` file
+
+## Getting Started
+
+From your terminal:
+
+```sh
+pnpm install
+pnpm dev
+```
+
+This starts your app in development mode, rebuilding assets on file changes.
+
+## Build
+
+To build the app for production:
+
+```sh
+pnpm build
+```
+
+## Authentication Configuration
+
+This example demonstrates how to integrate Better Auth with TanStack Start. Check the source code for examples of:
+
+- Configuring social authentication providers
+- Protecting routes with authentication
+- Accessing session data in server functions
+
+### Key Files
+
+- `src/utils/auth.ts` - Better Auth server configuration
+- `src/utils/auth-client.ts` - Better Auth client setup
+- `src/routes/__root.tsx` - Session fetching and navigation
+- `src/routes/protected.tsx` - Example of a protected route
diff --git a/examples/react/start-basic-better-auth/package.json b/examples/react/start-basic-better-auth/package.json
new file mode 100644
index 0000000000..eec836df46
--- /dev/null
+++ b/examples/react/start-basic-better-auth/package.json
@@ -0,0 +1,32 @@
+{
+  "name": "tanstack-start-example-basic-better-auth",
+  "private": true,
+  "sideEffects": false,
+  "type": "module",
+  "scripts": {
+    "dev": "vite dev",
+    "build": "vite build",
+    "preview": "vite preview",
+    "start": "pnpx srvx --prod -s ../client dist/server/server.js"
+  },
+  "dependencies": {
+    "@tanstack/react-router": "^1.147.0",
+    "@tanstack/react-router-devtools": "^1.147.0",
+    "@tanstack/react-start": "^1.147.0",
+    "better-auth": "^1.4.10",
+    "react": "^19.0.0",
+    "react-dom": "^19.0.0",
+    "tailwind-merge": "^2.6.0"
+  },
+  "devDependencies": {
+    "@tailwindcss/vite": "^4.1.18",
+    "@types/node": "^22.5.4",
+    "@types/react": "^19.0.8",
+    "@types/react-dom": "^19.0.3",
+    "@vitejs/plugin-react": "^4.6.0",
+    "tailwindcss": "^4.1.18",
+    "typescript": "^5.7.2",
+    "vite": "^7.1.7",
+    "vite-tsconfig-paths": "^5.1.4"
+  }
+}
diff --git a/examples/react/start-basic-better-auth/src/components/DefaultCatchBoundary.tsx b/examples/react/start-basic-better-auth/src/components/DefaultCatchBoundary.tsx
new file mode 100644
index 0000000000..2b0e48c6c8
--- /dev/null
+++ b/examples/react/start-basic-better-auth/src/components/DefaultCatchBoundary.tsx
@@ -0,0 +1,53 @@
+import {
+  ErrorComponent,
+  Link,
+  rootRouteId,
+  useMatch,
+  useRouter,
+} from "@tanstack/react-router";
+import type { ErrorComponentProps } from "@tanstack/react-router";
+
+export function DefaultCatchBoundary({ error }: ErrorComponentProps) {
+  const router = useRouter();
+  const isRoot = useMatch({
+    strict: false,
+    select: (state) => state.id === rootRouteId,
+  });
+
+  console.error("DefaultCatchBoundary Error:", error);
+
+  return (
+    <div className="min-w-0 flex-1 p-4 flex flex-col items-center justify-center gap-6">
+      <ErrorComponent error={error} />
+      <div className="flex gap-2 items-center flex-wrap">
+        <button
+          onClick={() => {
+            router.invalidate();
+          }}
+          className={`px-2 py-1 bg-gray-600 dark:bg-gray-700 rounded-sm text-white uppercase font-extrabold`}
+        >
+          Try Again
+        </button>
+        {isRoot ? (
+          <Link
+            to="/"
+            className={`px-2 py-1 bg-gray-600 dark:bg-gray-700 rounded-sm text-white uppercase font-extrabold`}
+          >
+            Home
+          </Link>
+        ) : (
+          <Link
+            to="/"
+            className={`px-2 py-1 bg-gray-600 dark:bg-gray-700 rounded-sm text-white uppercase font-extrabold`}
+            onClick={(e) => {
+              e.preventDefault();
+              window.history.back();
+            }}
+          >
+            Go Back
+          </Link>
+        )}
+      </div>
+    </div>
+  );
+}
diff --git a/examples/react/start-basic-better-auth/src/components/NotFound.tsx b/examples/react/start-basic-better-auth/src/components/NotFound.tsx
new file mode 100644
index 0000000000..f22fabddca
--- /dev/null
+++ b/examples/react/start-basic-better-auth/src/components/NotFound.tsx
@@ -0,0 +1,25 @@
+import { Link } from "@tanstack/react-router";
+
+export function NotFound({ children }: { children?: any }) {
+  return (
+    <div className="space-y-2 p-2">
+      <div className="text-gray-600 dark:text-gray-400">
+        {children || <p>The page you are looking for does not exist.</p>}
+      </div>
+      <p className="flex items-center gap-2 flex-wrap">
+        <button
+          onClick={() => window.history.back()}
+          className="bg-emerald-500 text-white px-2 py-1 rounded-sm uppercase font-black text-sm"
+        >
+          Go back
+        </button>
+        <Link
+          to="/"
+          className="bg-cyan-600 text-white px-2 py-1 rounded-sm uppercase font-black text-sm"
+        >
+          Start Over
+        </Link>
+      </p>
+    </div>
+  );
+}
diff --git a/examples/react/start-basic-better-auth/src/routeTree.gen.ts b/examples/react/start-basic-better-auth/src/routeTree.gen.ts
new file mode 100644
index 0000000000..2f59e4ba33
--- /dev/null
+++ b/examples/react/start-basic-better-auth/src/routeTree.gen.ts
@@ -0,0 +1,122 @@
+/* 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 ProtectedRouteImport } from './routes/protected'
+import { Route as LoginRouteImport } from './routes/login'
+import { Route as IndexRouteImport } from './routes/index'
+import { Route as ApiAuthSplatRouteImport } from './routes/api/auth/$'
+
+const ProtectedRoute = ProtectedRouteImport.update({
+  id: '/protected',
+  path: '/protected',
+  getParentRoute: () => rootRouteImport,
+} as any)
+const LoginRoute = LoginRouteImport.update({
+  id: '/login',
+  path: '/login',
+  getParentRoute: () => rootRouteImport,
+} as any)
+const IndexRoute = IndexRouteImport.update({
+  id: '/',
+  path: '/',
+  getParentRoute: () => rootRouteImport,
+} as any)
+const ApiAuthSplatRoute = ApiAuthSplatRouteImport.update({
+  id: '/api/auth/$',
+  path: '/api/auth/$',
+  getParentRoute: () => rootRouteImport,
+} as any)
+
+export interface FileRoutesByFullPath {
+  '/': typeof IndexRoute
+  '/login': typeof LoginRoute
+  '/protected': typeof ProtectedRoute
+  '/api/auth/$': typeof ApiAuthSplatRoute
+}
+export interface FileRoutesByTo {
+  '/': typeof IndexRoute
+  '/login': typeof LoginRoute
+  '/protected': typeof ProtectedRoute
+  '/api/auth/$': typeof ApiAuthSplatRoute
+}
+export interface FileRoutesById {
+  __root__: typeof rootRouteImport
+  '/': typeof IndexRoute
+  '/login': typeof LoginRoute
+  '/protected': typeof ProtectedRoute
+  '/api/auth/$': typeof ApiAuthSplatRoute
+}
+export interface FileRouteTypes {
+  fileRoutesByFullPath: FileRoutesByFullPath
+  fullPaths: '/' | '/login' | '/protected' | '/api/auth/$'
+  fileRoutesByTo: FileRoutesByTo
+  to: '/' | '/login' | '/protected' | '/api/auth/$'
+  id: '__root__' | '/' | '/login' | '/protected' | '/api/auth/$'
+  fileRoutesById: FileRoutesById
+}
+export interface RootRouteChildren {
+  IndexRoute: typeof IndexRoute
+  LoginRoute: typeof LoginRoute
+  ProtectedRoute: typeof ProtectedRoute
+  ApiAuthSplatRoute: typeof ApiAuthSplatRoute
+}
+
+declare module '@tanstack/react-router' {
+  interface FileRoutesByPath {
+    '/protected': {
+      id: '/protected'
+      path: '/protected'
+      fullPath: '/protected'
+      preLoaderRoute: typeof ProtectedRouteImport
+      parentRoute: typeof rootRouteImport
+    }
+    '/login': {
+      id: '/login'
+      path: '/login'
+      fullPath: '/login'
+      preLoaderRoute: typeof LoginRouteImport
+      parentRoute: typeof rootRouteImport
+    }
+    '/': {
+      id: '/'
+      path: '/'
+      fullPath: '/'
+      preLoaderRoute: typeof IndexRouteImport
+      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,
+  LoginRoute: LoginRoute,
+  ProtectedRoute: ProtectedRoute,
+  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>>
+  }
+}
diff --git a/examples/react/start-basic-better-auth/src/router.tsx b/examples/react/start-basic-better-auth/src/router.tsx
new file mode 100644
index 0000000000..338900bf6f
--- /dev/null
+++ b/examples/react/start-basic-better-auth/src/router.tsx
@@ -0,0 +1,19 @@
+import { createRouter } from "@tanstack/react-router";
+import { routeTree } from "./routeTree.gen";
+import { DefaultCatchBoundary } from "./components/DefaultCatchBoundary";
+import { NotFound } from "./components/NotFound";
+import type { RouterContext } from "./routes/__root";
+
+export function getRouter() {
+  const router = createRouter({
+    routeTree,
+    defaultPreload: "intent",
+    defaultErrorComponent: DefaultCatchBoundary,
+    defaultNotFoundComponent: () => <NotFound />,
+    scrollRestoration: true,
+    context: {
+      session: null,
+    } satisfies RouterContext,
+  });
+  return router;
+}
diff --git a/examples/react/start-basic-better-auth/src/routes/__root.tsx b/examples/react/start-basic-better-auth/src/routes/__root.tsx
new file mode 100644
index 0000000000..ff38c30e0c
--- /dev/null
+++ b/examples/react/start-basic-better-auth/src/routes/__root.tsx
@@ -0,0 +1,125 @@
+/// <reference types="vite/client" />
+import type { ReactNode } from "react";
+import {
+  HeadContent,
+  Link,
+  Outlet,
+  Scripts,
+  createRootRouteWithContext,
+  useRouter,
+} from "@tanstack/react-router";
+import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
+import { createServerFn } from "@tanstack/react-start";
+import { getRequestHeaders } from "@tanstack/react-start/server";
+import { auth, type AuthSession } from "~/utils/auth";
+import { authClient } from "~/utils/auth-client";
+import appCss from "~/styles/app.css?url";
+
+export interface RouterContext {
+  session: AuthSession | null;
+}
+
+const fetchSession = createServerFn({ method: "GET" }).handler(async () => {
+  const headers = getRequestHeaders();
+  const session = await auth.api.getSession({ headers });
+  return session;
+});
+
+export const Route = createRootRouteWithContext<RouterContext>()({
+  beforeLoad: async () => {
+    const session = await fetchSession();
+    return {
+      session,
+    };
+  },
+  head: () => ({
+    meta: [
+      {
+        charSet: "utf-8",
+      },
+      {
+        name: "viewport",
+        content: "width=device-width, initial-scale=1",
+      },
+      {
+        title: "TanStack Start Auth Example",
+      },
+    ],
+    links: [{ rel: "stylesheet", href: appCss }],
+  }),
+  component: RootComponent,
+});
+
+function RootComponent() {
+  return (
+    <RootDocument>
+      <Outlet />
+    </RootDocument>
+  );
+}
+
+function RootDocument({ children }: { children: ReactNode }) {
+  return (
+    <html>
+      <head>
+        <HeadContent />
+      </head>
+      <body>
+        <NavBar />
+        <main className="p-4">{children}</main>
+        <TanStackRouterDevtools position="bottom-right" />
+        <Scripts />
+      </body>
+    </html>
+  );
+}
+
+function NavBar() {
+  const router = useRouter();
+  const routeContext = Route.useRouteContext();
+
+  const handleSignOut = async () => {
+    await authClient.signOut();
+    await router.invalidate();
+    await router.navigate({ to: "/" });
+  };
+
+  return (
+    <nav className="p-4 flex gap-4 items-center bg-gray-100">
+      <Link
+        to="/"
+        activeProps={{ className: "font-bold" }}
+        activeOptions={{ exact: true }}
+      >
+        Home
+      </Link>
+      <Link to="/protected" activeProps={{ className: "font-bold" }}>
+        Protected
+      </Link>
+      <div className="ml-auto flex items-center gap-4">
+        {routeContext.session ? (
+          <>
+            <span className="text-gray-600">
+              {routeContext.session?.user?.name ||
+                routeContext.session?.user?.email}
+            </span>
+            <button
+              type="button"
+              onClick={handleSignOut}
+              className="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600 cursor-pointer"
+            >
+              Sign Out
+            </button>
+          </>
+        ) : (
+          <Link
+            to="/login"
+            className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
+          >
+            Sign In
+          </Link>
+        )}
+      </div>
+    </nav>
+  );
+}
diff --git a/examples/react/start-basic-better-auth/src/routes/api/auth/$.ts b/examples/react/start-basic-better-auth/src/routes/api/auth/$.ts
new file mode 100644
index 0000000000..79a36b6164
--- /dev/null
+++ b/examples/react/start-basic-better-auth/src/routes/api/auth/$.ts
@@ -0,0 +1,15 @@
+import { createFileRoute } from "@tanstack/react-router";
+import { auth } from "~/utils/auth";
+
+/**
+ * Better Auth API route handler
+ * Handles all auth routes: /api/auth/*
+ */
+export const Route = createFileRoute("/api/auth/$")({
+  server: {
+    handlers: {
+      GET: ({ request }) => auth.handler(request),
+      POST: ({ request }) => auth.handler(request),
+    },
+  },
+});
diff --git a/examples/react/start-basic-better-auth/src/routes/index.tsx b/examples/react/start-basic-better-auth/src/routes/index.tsx
new file mode 100644
index 0000000000..afc0c137f2
--- /dev/null
+++ b/examples/react/start-basic-better-auth/src/routes/index.tsx
@@ -0,0 +1,50 @@
+import { createFileRoute } from "@tanstack/react-router";
+
+export const Route = createFileRoute("/")({
+  component: Home,
+});
+
+function Home() {
+  const { session } = Route.useRouteContext();
+  const user = session?.user;
+
+  return (
+    <div className="max-w-2xl mx-auto">
+      <h1 className="text-3xl font-bold mb-4">
+        TanStack Start Better Auth Example
+      </h1>
+      <p className="text-gray-600 mb-8">
+        This example demonstrates Better Auth integration with TanStack Start
+        using GitHub OAuth.
+      </p>
+
+      <div className="bg-gray-50 rounded-lg p-6">
+        <h2 className="text-xl font-semibold mb-4">Auth Status</h2>
+
+        {session ? (
+          <div className="space-y-2">
+            <p className="text-green-600 font-medium">Authenticated</p>
+            {user?.image && (
+              <img
+                src={user.image}
+                alt="Avatar"
+                className="w-16 h-16 rounded-full"
+              />
+            )}
+            <p>
+              <strong>Name:</strong> {user?.name ?? "N/A"}
+            </p>
+            <p>
+              <strong>Email:</strong> {user?.email ?? "N/A"}
+            </p>
+          </div>
+        ) : (
+          <p className="text-gray-500">
+            You are not signed in. Click "Sign In" in the navigation bar to
+            authenticate with GitHub.
+          </p>
+        )}
+      </div>
+    </div>
+  );
+}
diff --git a/examples/react/start-basic-better-auth/src/routes/login.tsx b/examples/react/start-basic-better-auth/src/routes/login.tsx
new file mode 100644
index 0000000000..9f67a0bd78
--- /dev/null
+++ b/examples/react/start-basic-better-auth/src/routes/login.tsx
@@ -0,0 +1,43 @@
+import { createFileRoute, redirect } from "@tanstack/react-router";
+import { authClient } from "~/utils/auth-client";
+
+export const Route = createFileRoute("/login")({
+  beforeLoad: ({ context }) => {
+    if (context.session) {
+      throw redirect({ to: "/" });
+    }
+  },
+  component: Login,
+});
+
+function Login() {
+  const handleGitHubSignIn = () => {
+    authClient.signIn.social({
+      provider: "github",
+      callbackURL: "/",
+    });
+  };
+
+  return (
+    <div className="max-w-md mx-auto mt-10">
+      <h1 className="text-2xl font-bold mb-6 text-center">Sign In</h1>
+
+      <div className="space-y-4">
+        <button
+          type="button"
+          onClick={handleGitHubSignIn}
+          className="w-full flex items-center justify-center gap-3 px-4 py-3 bg-gray-900 text-white rounded-lg hover:bg-gray-800 transition-colors cursor-pointer"
+        >
+          <svg className="w-5 h-5" viewBox="0 0 24 24" fill="currentColor">
+            <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
+          </svg>
+          Continue with GitHub
+        </button>
+
+        <p className="text-center text-sm text-gray-500 mt-4">
+          You'll be redirected to GitHub to complete the sign-in process.
+        </p>
+      </div>
+    </div>
+  );
+}
diff --git a/examples/react/start-basic-better-auth/src/routes/protected.tsx b/examples/react/start-basic-better-auth/src/routes/protected.tsx
new file mode 100644
index 0000000000..05e8ebd83c
--- /dev/null
+++ b/examples/react/start-basic-better-auth/src/routes/protected.tsx
@@ -0,0 +1,55 @@
+import { createFileRoute, redirect } from "@tanstack/react-router";
+
+export const Route = createFileRoute("/protected")({
+  beforeLoad: ({ context }) => {
+    if (!context.session) {
+      throw redirect({ to: "/login" });
+    }
+  },
+  component: Protected,
+});
+
+function Protected() {
+  const { session } = Route.useRouteContext();
+  const user = session?.user;
+
+  return (
+    <div className="max-w-2xl mx-auto">
+      <h1 className="text-3xl font-bold mb-4">Protected Page</h1>
+      <p className="text-gray-600 mb-8">
+        This page is only accessible to authenticated users.
+      </p>
+
+      <div className="bg-green-50 border border-green-200 rounded-lg p-6">
+        <h2 className="text-xl font-semibold mb-4 text-green-800">
+          Welcome, {user?.name ?? "User"}!
+        </h2>
+
+        {user && (
+          <div className="space-y-2 text-green-700">
+            <p>
+              <strong>Email:</strong> {user?.email ?? "N/A"}
+            </p>
+            {user?.image && (
+              <div>
+                <strong>Avatar:</strong>
+                <img
+                  src={user.image}
+                  alt="User avatar"
+                  className="w-20 h-20 rounded-full mt-2"
+                />
+              </div>
+            )}
+          </div>
+        )}
+      </div>
+
+      <div className="mt-8 p-4 bg-gray-100 rounded-lg">
+        <h3 className="font-semibold mb-2">Session Data (Debug)</h3>
+        <pre className="text-xs overflow-auto bg-gray-800 text-green-400 p-4 rounded">
+          {JSON.stringify(session, null, 2)}
+        </pre>
+      </div>
+    </div>
+  );
+}
diff --git a/examples/react/start-basic-better-auth/src/styles/app.css b/examples/react/start-basic-better-auth/src/styles/app.css
new file mode 100644
index 0000000000..f1d8c73cdc
--- /dev/null
+++ b/examples/react/start-basic-better-auth/src/styles/app.css
@@ -0,0 +1 @@
+@import "tailwindcss";
diff --git a/examples/react/start-basic-better-auth/src/utils/auth-client.ts b/examples/react/start-basic-better-auth/src/utils/auth-client.ts
new file mode 100644
index 0000000000..f1012dd4ac
--- /dev/null
+++ b/examples/react/start-basic-better-auth/src/utils/auth-client.ts
@@ -0,0 +1,3 @@
+import { createAuthClient } from "better-auth/react";
+
+export const authClient = createAuthClient();
diff --git a/examples/react/start-basic-better-auth/src/utils/auth.ts b/examples/react/start-basic-better-auth/src/utils/auth.ts
new file mode 100644
index 0000000000..05cf771468
--- /dev/null
+++ b/examples/react/start-basic-better-auth/src/utils/auth.ts
@@ -0,0 +1,28 @@
+import { betterAuth } from "better-auth";
+import { tanstackStartCookies } from "better-auth/tanstack-start";
+
+/**
+ * Better Auth configuration for TanStack Start with GitHub
+ */
+export const auth = betterAuth({
+  socialProviders: {
+    github: {
+      clientId: process.env.GITHUB_CLIENT_ID!,
+      clientSecret: process.env.GITHUB_CLIENT_SECRET!,
+    },
+  },
+  plugins: [
+    // Must be last plugin in the array
+    tanstackStartCookies(),
+  ],
+  // Stateless sessions (no database required)
+  session: {
+    cookieCache: {
+      enabled: true,
+      maxAge: 60 * 60 * 24 * 7,
+    },
+  },
+});
+
+// Export session type for use in router context
+export type AuthSession = typeof auth.$Infer.Session;
diff --git a/examples/react/start-basic-better-auth/tsconfig.json b/examples/react/start-basic-better-auth/tsconfig.json
new file mode 100644
index 0000000000..70cbbc5ce3
--- /dev/null
+++ b/examples/react/start-basic-better-auth/tsconfig.json
@@ -0,0 +1,24 @@
+{
+  "compilerOptions": {
+    "target": "ES2022",
+    "lib": ["ES2022", "DOM", "DOM.Iterable"],
+    "module": "ESNext",
+    "skipLibCheck": true,
+    "moduleResolution": "bundler",
+    "allowImportingTsExtensions": true,
+    "resolveJsonModule": true,
+    "isolatedModules": true,
+    "noEmit": true,
+    "jsx": "react-jsx",
+    "strict": true,
+    "noUnusedLocals": true,
+    "noUnusedParameters": true,
+    "noFallthroughCasesInSwitch": true,
+    "allowSyntheticDefaultImports": true,
+    "baseUrl": ".",
+    "paths": {
+      "~/*": ["./src/*"]
+    }
+  },
+  "include": ["src", "env.d.ts", "vite.config.ts"]
+}
diff --git a/examples/react/start-basic-better-auth/vite.config.ts b/examples/react/start-basic-better-auth/vite.config.ts
new file mode 100644
index 0000000000..3a326e95cf
--- /dev/null
+++ b/examples/react/start-basic-better-auth/vite.config.ts
@@ -0,0 +1,20 @@
+import { tanstackStart } from "@tanstack/react-start/plugin/vite";
+import { defineConfig } from "vite";
+import tsConfigPaths from "vite-tsconfig-paths";
+import viteReact from "@vitejs/plugin-react";
+import tailwindcss from "@tailwindcss/vite";
+
+export default defineConfig({
+  server: {
+    port: 10000,
+    strictPort: true,
+  },
+  plugins: [
+    tailwindcss(),
+    tsConfigPaths({
+      projects: ["./tsconfig.json"],
+    }),
+    tanstackStart(),
+    viteReact(),
+  ],
+});
