Help us improve
Share bugs, ideas, or general feedback.
From tanstack-start-guide
Guides setup, configuration, and usage of TanStack Start full-stack React framework with SSR, streaming, server functions, routes, middleware, and Vite. For app creation, deployment, auth, Tailwind integration, and Next.js migrations.
npx claudepluginhub vcode-sh/vibe-tools --plugin tanstack-start-guideHow this skill is triggered — by the user, by Claude, or both
Slash command
/tanstack-start-guide:tanstack-start-guideThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
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.
references/api-middleware.mdreferences/api-routing.mdreferences/api-server-functions.mdreferences/auth-sessions.mdreferences/configuration.mdreferences/data-streaming.mdreferences/deployment.mdreferences/migration.mdreferences/observability.mdreferences/patterns.mdreferences/prerendering-caching.mdreferences/seo-llmo.mdreferences/server-entry.mdreferences/server-routes.mdreferences/troubleshooting.mdSets up TanStack Start full-stack React apps with SSR, streaming, server functions, API routes, and Nitro-based deployment to any platform. Covers project structure, config, and type-safe RPC.
Guides TanStack Start full-stack React setup with Bun, covering file-based routing, TanStack Router, server functions, loaders, and vinxi SSR/build troubleshooting.
Generates full-stack TanStack Start app on Cloudflare Workers: SSR, routing, D1+Drizzle DB, better-auth, Tailwind v4+shadcn/ui. Fresh file generation per project.
Share bugs, ideas, or general feedback.
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.
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.
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
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" } }
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>
}
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>
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],
}))
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 })
},
},
},
})
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' },
})
}
Loaders are ISOMORPHIC — they run on server during SSR AND on client during navigation. Never access process.env secrets directly in loaders. Use createServerFn() instead.
Environment variables — Server: process.env.ANY_VAR. Client: only import.meta.env.VITE_* prefixed. Never prefix secrets with VITE_.
Plugin order — tanstackStart() MUST come before viteReact() 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. Use createClientOnlyFn() for browser-only utilities. Use createIsomorphicFn() 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 Response objects directly for binary data or custom content types.
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>
),
})
Date.now(), Math.random(), locale-dependent APIs in SSR. Fix: use <ClientOnly>, useHydrated(), or suppressHydrationWarning.VITE_ prefix. Restart dev server after adding new vars.process.env in loader (isomorphic!). Move to createServerFn().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