--- name: tanstack-start description: >- TanStack Start full-stack React framework. Use for: server functions with createServerFn, TanStack Router file-based routing, TanStack Query SSR integration, Cloudflare Workers deployment. --- # TanStack Start Full-stack React framework with SSR, server functions, and Vite bundling. ## Project Setup **New project:** ```bash pnpm create cloudflare@latest my-app --framework=tanstack-start -y --no-deploy ``` **Existing app:** See [references/migration.md](references/migration.md) for converting React/Vite apps. ## Critical Configuration ### vite.config.ts ```typescript import { defineConfig } from 'vite' import tsConfigPaths from 'vite-tsconfig-paths' import { tanstackStart } from '@tanstack/react-start/plugin/vite' import viteReact from '@vitejs/plugin-react' export default defineConfig({ plugins: [ tsConfigPaths(), tanstackStart(), viteReact(), // MUST come AFTER tanstackStart ], }) ``` ### tsconfig.json gotcha Do NOT enable `verbatimModuleSyntax` - it leaks server bundles into client bundles. ## Server Functions See [references/server-functions.md](references/server-functions.md) for complete guide. > ⚠️ **Critical**: Route loaders run on server for initial SSR, but run on **CLIENT** during navigation. Always wrap server code in `createServerFn()` to ensure it runs server-side. **When to use what (Cloudflare Workers):** | Use Case | Solution | |----------|----------| | Server code in route loaders | `createServerFn()` | | Server code from client event handlers | API routes (`server.handlers`) work best | | Access Cloudflare bindings | `import { env } from 'cloudflare:workers'` | **createServerFn** - for loaders: ```typescript import { createServerFn } from '@tanstack/react-start' export const getData = createServerFn().handler(async () => { return { data: process.env.SECRET } // Server-only }) ``` **API routes** - for client event handlers: ```tsx // routes/api/users.ts export const Route = createFileRoute('/api/users')({ server: { handlers: { POST: async ({ request }) => { const body = await request.json() return Response.json(await db.users.create(body)) }, }, }, }) // In component: fetch('/api/users', { method: 'POST', body: JSON.stringify(data) }) ``` **Key APIs:** - `createServerFn()` - Server-only functions for loaders - `server.handlers` - API routes for client event handlers - `createMiddleware({ type: 'function' })` - Reusable middleware - `@tanstack/react-start/server`: `getRequestHeaders()`, `setResponseHeader()`, `getCookies()` ## Routing See [references/routing.md](references/routing.md) for complete patterns. **File conventions** in `src/routes/`: | Pattern | Route | |---------|-------| | `index.tsx` | `/` | | `posts.$postId.tsx` | `/posts/:postId` | | `_layout.tsx` | Layout (no URL) | | `__root.tsx` | Root layout (required) | ```tsx import { createFileRoute } from '@tanstack/react-router' import { createServerFn } from '@tanstack/react-start' const getPost = createServerFn() .handler(async () => await db.post.findFirst()) export const Route = createFileRoute('/posts/$postId')({ loader: ({ params }) => getPost({ data: params.postId }), component: () => { const post = Route.useLoaderData() return