원클릭으로
start-core-deployment
// Deploy to Cloudflare Workers, Netlify, Vercel, Node.js/Docker, Bun, Railway. Selective SSR (ssr option per route), SPA mode, static prerendering, ISR with Cache-Control headers, SEO and head management.
// Deploy to Cloudflare Workers, Netlify, Vercel, Node.js/Docker, Bun, Railway. Selective SSR (ssr option per route), SPA mode, static prerendering, ISR with Cache-Control headers, SEO and head management.
Server-side runtime for TanStack Start: createStartHandler, request/response utilities (getRequest, setResponseHeader, setCookie, getCookie, useSession), three-phase request handling, AsyncLocalStorage context.
Route protection with beforeLoad, redirect()/throw redirect(), isRedirect helper, authenticated layout routes (_authenticated), non-redirect auth (inline login), RBAC with roles and permissions, auth provider integration (Auth0, Clerk, Supabase), router context for auth state.
Framework-agnostic core concepts for TanStack Router: route trees, createRouter, createRoute, createRootRoute, createRootRouteWithContext, addChildren, Register type declaration, route matching, route sorting, file naming conventions. Entry point for all router skills.
Non-streaming and streaming SSR, RouterClient/RouterServer, renderRouterToString/renderRouterToStream, createRequestHandler, defaultRenderHandler/defaultStreamHandler, HeadContent/Scripts components, head route option (meta/links/styles/scripts), ScriptOnce, automatic loader dehydration/hydration, memory history on server, data serialization, document head management.
Full type inference philosophy (never cast, never annotate inferred values), Register module declaration, from narrowing on hooks and Link, strict:false for shared components, getRouteApi for code-split typed access, addChildren with object syntax for TS perf, LinkProps and ValidateLinkOptions type utilities, as const satisfies pattern.
Server-side authentication primitives for TanStack Start: session cookies (HttpOnly, Secure, SameSite, __Host- prefix), session read/issue/destroy via createServerFn and middleware, OAuth authorization-code flow with state and PKCE, password-reset enumeration defense, CSRF for non-GET RPCs, rate limiting auth endpoints, session rotation on privilege change. Pairs with router-core/auth-and-guards for the routing side.
| name | start-core/deployment |
| description | Deploy to Cloudflare Workers, Netlify, Vercel, Node.js/Docker, Bun, Railway. Selective SSR (ssr option per route), SPA mode, static prerendering, ISR with Cache-Control headers, SEO and head management. |
| type | sub-skill |
| library | tanstack-start |
| library_version | 1.166.2 |
| requires | ["start-core"] |
| sources | ["TanStack/router:docs/start/framework/react/guide/hosting.md","TanStack/router:docs/start/framework/react/guide/selective-ssr.md","TanStack/router:docs/start/framework/react/guide/static-prerendering.md","TanStack/router:docs/start/framework/react/guide/seo.md"] |
TanStack Start deploys to any hosting provider via Vite and Nitro. This skill covers hosting setup, SSR configuration, prerendering, and SEO.
pnpm add -D @cloudflare/vite-plugin wrangler
// vite.config.ts
import { defineConfig } from 'vite'
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
import { cloudflare } from '@cloudflare/vite-plugin'
import viteReact from '@vitejs/plugin-react'
export default defineConfig({
plugins: [
cloudflare({ viteEnvironment: { name: 'ssr' } }),
tanstackStart(),
viteReact(),
],
})
// wrangler.jsonc
{
"name": "my-app",
"compatibility_date": "2025-09-02",
"compatibility_flags": ["nodejs_compat"],
"main": "@tanstack/react-start/server-entry",
}
Deploy: npx wrangler login && pnpm run deploy
Worker env is per-request. Cloudflare Workers inject env vars at request time.
process.env.Xat module scope evaluates toundefinedeven on the server. The Cloudflare-canonical way to read env (including from module scope) is thecloudflare:workersenv binding:import { env } from 'cloudflare:workers' const apiHost = env.API_HOSTOr read
process.env.Xper-request inside.handler()/ middleware.server(). See Cloudflare's environment-variables docs and start-core/execution-model.
pnpm add -D @netlify/vite-plugin-tanstack-start
// vite.config.ts
import { defineConfig } from 'vite'
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
import netlify from '@netlify/vite-plugin-tanstack-start'
import viteReact from '@vitejs/plugin-react'
export default defineConfig({
plugins: [tanstackStart(), netlify(), viteReact()],
})
Deploy: npx netlify deploy
npm install nitro@npm:nitro-nightly@latest
// vite.config.ts
import { defineConfig } from 'vite'
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
import { nitro } from 'nitro/vite'
import viteReact from '@vitejs/plugin-react'
export default defineConfig({
plugins: [tanstackStart(), nitro(), viteReact()],
})
Build and start: npm run build && node .output/server/index.mjs
Bun deployment requires React 19. For React 18, use Node.js deployment.
// vite.config.ts — add bun preset to nitro
plugins: [tanstackStart(), nitro({ preset: 'bun' }), viteReact()]
Control SSR per route with the ssr property.
ssr: true (default)Runs beforeLoad and loader on server, renders component on server:
export const Route = createFileRoute('/posts/$postId')({
ssr: true, // default
loader: () => fetchPost(), // runs on server during SSR
component: PostPage, // rendered on server
})
ssr: falseDisables server execution of beforeLoad/loader and server rendering:
export const Route = createFileRoute('/dashboard')({
ssr: false,
loader: () => fetchDashboard(), // runs on client only
component: DashboardPage, // rendered on client only
})
ssr: 'data-only'Runs beforeLoad/loader on server but renders component on client only:
export const Route = createFileRoute('/canvas')({
ssr: 'data-only',
loader: () => fetchCanvasData(), // runs on server
component: CanvasPage, // rendered on client only
})
Decide SSR at runtime based on params/search:
export const Route = createFileRoute('/docs/$docType/$docId')({
ssr: ({ params }) => {
if (params.status === 'success' && params.value.docType === 'sheet') {
return false
}
},
})
Children inherit parent SSR config and can only be MORE restrictive:
true → data-only or false (allowed)false → true (NOT allowed — parent false wins)Change the default for all routes in src/start.ts:
import { createStart } from '@tanstack/react-start'
export const startInstance = createStart(() => ({
defaultSsr: false,
}))
Generate static HTML at build time:
// vite.config.ts
tanstackStart({
prerender: {
enabled: true,
crawlLinks: true,
concurrency: 14,
failOnError: true,
},
})
Static routes are auto-discovered. Dynamic routes (e.g. /users/$userId) require crawlLinks or explicit pages config.
export const Route = createFileRoute('/')({
head: () => ({
meta: [
{ title: 'My App - Home' },
{ name: 'description', content: 'Welcome to My App' },
],
}),
})
export const Route = createFileRoute('/posts/$postId')({
loader: async ({ params }) => fetchPost(params.postId),
head: ({ loaderData }) => ({
meta: [
{ title: loaderData.title },
{ name: 'description', content: loaderData.excerpt },
{ property: 'og:title', content: loaderData.title },
{ property: 'og:image', content: loaderData.coverImage },
],
}),
})
head: ({ loaderData }) => ({
scripts: [
{
type: 'application/ld+json',
children: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'Article',
headline: loaderData.title,
}),
},
],
})
// src/routes/sitemap[.]xml.ts
export const Route = createFileRoute('/sitemap.xml')({
server: {
handlers: {
GET: async () => {
const posts = await fetchAllPosts()
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${posts.map((p) => `<url><loc>https://myapp.com/posts/${p.id}</loc></url>`).join('')}
</urlset>`
return new Response(sitemap, {
headers: { 'Content-Type': 'application/xml' },
})
},
},
},
})
// WRONG — Node.js APIs fail at runtime
{ "compatibility_flags": [] }
// CORRECT
{ "compatibility_flags": ["nodejs_compat"] }
Bun-specific deployment only works with React 19. Use Node.js deployment for React 18.
// Parent sets ssr: false
// WRONG — child cannot upgrade to ssr: true
const parentRoute = createFileRoute('/dashboard')({ ssr: false })
const childRoute = createFileRoute('/dashboard/stats')({
ssr: true, // IGNORED — parent false wins
})
// CORRECT — children can only be MORE restrictive
const parentRoute = createFileRoute('/dashboard')({ ssr: 'data-only' })
const childRoute = createFileRoute('/dashboard/stats')({
ssr: false, // OK — more restrictive than parent
})