tanstack-start-guide
TanStack Start Guide
TanStack Start is a full-stack React framework powered by TanStack Router and Vite. It provides SSR, streaming, server functions, server routes, middleware, and universal deployment. If you only need client-side routing without SSR, streaming, server functions, or middleware, use TanStack Router directly instead of Start. Not for Next.js, Remix, or React Router.
Key differentiators: End-to-end type safety, composable middleware (client + server), selective SSR per route, deployment-agnostic (any Vite-compatible host), explicit over implicit patterns.
RSC support: Available via Composite Components — server-produced React components that the client fetches, caches, and streams. See references/migration.md for details.
Quick Start
pnpm create @tanstack/start@latest
# or
npm create @tanstack/start@latest
Or clone an official example:
npx gitpick TanStack/router/tree/main/examples/react/EXAMPLE_SLUG my-project
Official examples: start-basic, start-basic-auth, start-counter, start-basic-react-query, start-clerk-basic, start-convex-trellaux, start-supabase-basic, start-trellaux, start-workos, start-material-ui.
Manual Setup
Install dependencies:
npm i @tanstack/react-start @tanstack/react-router react react-dom
npm i -D vite @vitejs/plugin-react typescript vite-tsconfig-paths @types/node @types/react @types/react-dom
Required Files
vite.config.ts — Vite plugin configuration:
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()
],
})
Alternative React plugins:
@vitejs/plugin-react-swcor@vitejs/plugin-react-oxccan replace@vitejs/plugin-react.
src/router.tsx — Router configuration:
import { createRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'
export function getRouter() {
return createRouter({ routeTree, scrollRestoration: true })
}
src/routes/__root.tsx — Root route (HTML shell):
/// <reference types="vite/client" />
import type { ReactNode } from 'react'
import { Outlet, createRootRoute, HeadContent, Scripts } from '@tanstack/react-router'
export const Route = createRootRoute({
head: () => ({
meta: [
{ charSet: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ title: 'My App' },
],
}),
component: () => (
<html><head><HeadContent /></head>
<body><Outlet /><Scripts /></body>
</html>
),
})
package.json scripts:
{ "type": "module", "scripts": { "dev": "vite dev", "build": "vite build" } }
Core Workflow
1. File-Based Routing
Routes live in src/routes/. The routeTree.gen.ts is auto-generated on dev/build.
| Path | Filename | Type |
|---|---|---|
/ |
index.tsx |
Index |
/about |
about.tsx |
Static |
/posts/:id |
posts/$postId.tsx |
Dynamic |
/rest/* |
rest/$.tsx |
Wildcard |
| Layout wrapper | _layout.tsx |
Pathless layout |
| Grouped dir | (group)/route.tsx |
Organization only |
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/posts/$postId')({
loader: async ({ params }) => fetchPost(params.postId),
component: PostComponent,
})
function PostComponent() {
const post = Route.useLoaderData()
return <h1>{post.title}</h1>
}
2. Server Functions
Server-only logic callable from anywhere. Created with createServerFn():
import { createServerFn } from '@tanstack/react-start'
export const getUser = createServerFn({ method: 'GET' })
.inputValidator((data: { id: string }) => data)
.handler(async ({ data }) => {
return await db.users.find(data.id) // Runs only on server
})
// Call from loader, component, or other server function
const user = await getUser({ data: { id: '123' } })
Validation with Zod:
import { z } from 'zod'
export const createPost = createServerFn({ method: 'POST' })
.inputValidator(z.object({ title: z.string().min(1), body: z.string() }))
.handler(async ({ data }) => db.posts.create(data))
Redirects & errors:
import { redirect, notFound } from '@tanstack/react-router'
export const requireAuth = createServerFn().handler(async () => {
const user = await getSession()
if (!user) throw redirect({ to: '/login' })
return user
})
Progressive enhancement (no JS):
// Server functions have a .url property for HTML forms
<form method="POST" action={createPost.url}>
<input name="title" />
<button type="submit">Create</button>
</form>
3. Middleware
Two types: request middleware (all requests) and server function middleware (server functions only).
import { createMiddleware } from '@tanstack/react-start'
// Request middleware (server only)
const logger = createMiddleware().server(async ({ next, request }) => {
console.log(request.url)
return next()
})
// Server function middleware (client + server)
const auth = createMiddleware({ type: 'function' })
.client(async ({ next }) => next({ headers: { Authorization: `Bearer ${getToken()}` } }))
.server(async ({ next }) => next({ context: { user: await getUser() } }))
Global middleware via src/start.ts:
import { createStart } from '@tanstack/react-start'
export const startInstance = createStart(() => ({
requestMiddleware: [loggerMiddleware],
functionMiddleware: [authMiddleware],
}))
4. Server Routes (API Endpoints)
export const Route = createFileRoute('/api/hello')({
server: {
handlers: {
GET: async ({ request }) => Response.json({ message: 'Hello!' }),
POST: async ({ request }) => {
const body = await request.json()
return Response.json({ received: body })
},
},
},
})
5. Sessions
import { useSession } from '@tanstack/react-start/server'
function useAppSession() {
return useSession<{ userId?: string }>({
password: process.env.SESSION_SECRET!,
cookie: { secure: process.env.NODE_ENV === 'production', httpOnly: true, sameSite: 'lax' },
})
}
Key Rules & Constraints
-
Loaders are ISOMORPHIC — they run on server during SSR AND on client during navigation. Never access
process.envsecrets directly in loaders. UsecreateServerFn()instead. -
Environment variables — Server:
process.env.ANY_VAR. Client: onlyimport.meta.env.VITE_*prefixed. Never prefix secrets withVITE_. -
Plugin order —
tanstackStart()MUST come beforeviteReact()in vite.config.ts. -
TypeScript — Do NOT enable
verbatimModuleSyntax(causes server bundle leaking into client). Required settings:jsx: "react-jsx",moduleResolution: "Bundler",module: "ESNext". -
Server function imports — Safe to statically import anywhere. Avoid dynamic imports for server functions.
-
Execution boundaries — Use
createServerOnlyFn()for server-only utilities that throw on client. UsecreateClientOnlyFn()for browser-only utilities. UsecreateIsomorphicFn()for environment-specific implementations. -
Head management —
<HeadContent />in<head>,<Scripts />at end of<body>. Both required in root route. -
Raw Response — Server functions can return
Responseobjects directly for binary data or custom content types.
Error Boundaries
Route-level error boundaries with default + per-route override:
// src/router.tsx — default for all routes
export function getRouter() {
return createRouter({
routeTree,
defaultErrorComponent: ({ error, reset }) => <ErrorComponent error={error} />,
})
}
// Per-route override
export const Route = createFileRoute('/posts/$postId')({
errorComponent: ({ error, reset }: ErrorComponentProps) => (
<div><p>Error: {error.message}</p><button onClick={reset}>Retry</button></div>
),
})
Common Errors
- Hydration mismatch: Caused by
Date.now(),Math.random(), locale-dependent APIs in SSR. Fix: use<ClientOnly>,useHydrated(), orsuppressHydrationWarning. - Env var undefined on client: Missing
VITE_prefix. Restart dev server after adding new vars. - Secret exposed to client: Used
process.envin loader (isomorphic!). Move tocreateServerFn(). - Bundle includes server code: Check for accidental dynamic imports of server functions.
Reference Files
references/api-routing.md— Routing, createFileRoute, createRootRoute, route hooks, components, error boundaries, navigationreferences/api-server-functions.md— createServerFn, validation, streaming, server context utilities, useSession, environment functions, useServerFnreferences/api-middleware.md— createMiddleware, createStart, custom fetch, header merging, fetch override, createHandlersreferences/server-entry.md— Custom server/client entry, request context, handler callbacks, Cloudflare Workers extensionsreferences/configuration.md— Vite config options, TypeScript, environment variables, path aliases, Tailwind CSS, server build configreferences/auth-sessions.md— Authentication (DIY + partners), sessions, route protection, RBAC, OAuth, password reset, rate limitingreferences/data-streaming.md— Data loading patterns, streaming (async generators, ReadableStream), cache control, TanStack Query integrationreferences/seo-llmo.md— SEO meta tags, JSON-LD structured data, LLMO/AIO optimization, sitemaps, robots.txt, llms.txtreferences/server-routes.md— API endpoints, dynamic params, wildcard routes, request bodies, per-handler middleware, escaped file namesreferences/patterns.md— Markdown rendering, database integration (Neon/Convex/Prisma), file organization, progressive enhancement, execution model, tutorialsreferences/deployment.md— Cloudflare Workers, Netlify, Railway, Vercel, Node.js/Docker, Bun, Appwrite Sites, Nitroreferences/prerendering-caching.md— Static prerendering (SSG), ISR, selective SSR, SPA mode, CDN asset URLsreferences/observability.md— Sentry, New Relic, OpenTelemetry, health checks, metrics collection, loggingreferences/migration.md— Next.js migration guide, framework comparison (Start vs Next.js vs React Router)references/troubleshooting.md— Hydration errors, env variable issues, loader mistakes, middleware problems, production checklist
More from vcode-sh/vibe-tools
orpc-guide
>-
80tanstack-router-guide
>
10hono-guide
Guide for Hono, an ultrafast web framework built on Web Standards. Use when user asks to "create a Hono app", "build an API with Hono", "add Hono middleware", "deploy Hono to Cloudflare Workers", "use Hono RPC", "add auth to Hono", "validate requests in Hono", "use Hono JSX", or asks about Hono routing, context, streaming, WebSocket, CORS, testing, SSG, or multi-runtime deployment. Do NOT use for Express.js, Fastify, Koa, or Nest.js.
9shadcn-guide
Guide for shadcn/ui — the open-code component system for React. Use when user asks to install, configure, theme, or use shadcn/ui components, set up dark mode, create forms, build a registry, configure MCP, or set up RTL/monorepo/JavaScript mode. Covers 58+ components (Base UI and Radix variants), CLI commands, components.json, CSS variable theming, framework installation (Next.js, Vite, Astro, Remix, Laravel, Gatsby, TanStack), registry system, and form patterns. Do NOT use for Chakra UI, Material UI, Ant Design, or vanilla Tailwind without shadcn context.
8tanstack-hotkeys-guide
>-
8base-ui-guide
Guide for Base UI (@base-ui/react), an unstyled React component library for building accessible UIs. Use when user asks to "build a component with Base UI", "create a form with Base UI", "style Base UI components", "animate Base UI popover", "use Base UI dialog", "add Base UI select", "implement Base UI tabs", or asks about Base UI accessibility, composition, customization, styling, animation, TypeScript types, or any of its 35+ components. Covers all components (Dialog, Menu, Popover, Select, Combobox, Tabs, Accordion, Toast, Form, Field, Slider, etc.), styling patterns, animations, composition via render props, event customization, form integration, and utilities. Do NOT use for Radix UI (radix-ui), Material UI (MUI @mui/material), or Shadcn/ui - those are separate libraries with different APIs.
6