tanstack/router

deployment

- Deploy to Cloudflare Workers, Netlify, Vercel, Node.js/Docker, Bun, Railway.

소스 보기
원본 Skill 문서

원본 저장소의 제목, 예시, 코드, 표, 링크, 이미지를 유지해 표시합니다.

Deployment and Rendering

TanStack Start deploys to any hosting provider via Vite and Nitro. This skill covers hosting setup, SSR configuration, prerendering, and SEO.

Hosting Providers

Cloudflare Workers

bash
pnpm add -D @cloudflare/vite-plugin wrangler
ts
// 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(),
  ],
})
jsonc
// 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.X at module scope evaluates to undefined even on the server. The Cloudflare-canonical way to read env (including from module scope) is the cloudflare:workers env binding: ``ts import { env } from 'cloudflare:workers' const apiHost = env.API_HOST Or read process.env.X per-request inside .handler() / middleware .server()`. See Cloudflare's environment-variables docs and start-core/execution-model.

Netlify

bash
pnpm add -D @netlify/vite-plugin-tanstack-start
ts
// 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

Nitro (Vercel, Railway, Node.js, Docker)

bash
npm install nitro@npm:nitro-nightly@latest
ts
// 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

Bun deployment requires React 19. For React 18, use Node.js deployment.

ts
// vite.config.ts — add bun preset to nitro
plugins: [tanstackStart(), nitro({ preset: 'bun' }), viteReact()]

Selective SSR

Control SSR per route with the ssr property.

ssr: true (default)

Runs beforeLoad and loader on server, renders component on server:

tsx
export const Route = createFileRoute('/posts/$postId')({
  ssr: true, // default
  loader: () => fetchPost(), // runs on server during SSR
  component: PostPage, // rendered on server
})

ssr: false

Disables server execution of beforeLoad/loader and server rendering:

tsx
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:

tsx
export const Route = createFileRoute('/canvas')({
  ssr: 'data-only',
  loader: () => fetchCanvasData(), // runs on server
  component: CanvasPage, // rendered on client only
})

Functional Form

Decide SSR at runtime based on params/search:

tsx
export const Route = createFileRoute('/docs/$docType/$docId')({
  ssr: ({ params }) => {
    if (params.status === 'success' && params.value.docType === 'sheet') {
      return false
    }
  },
})

SSR Inheritance

Children inherit parent SSR config and can only be MORE restrictive:

  • true → data-only or false (allowed)
  • false → true (NOT allowed — parent false wins)

Default SSR

Change the default for all routes in src/start.ts:

tsx
import { createStart } from '@tanstack/react-start'

export const startInstance = createStart(() => ({
  defaultSsr: false,
}))

Static Prerendering

Generate static HTML at build time:

ts
// 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.

SEO and Head Management

Basic Meta Tags

tsx
export const Route = createFileRoute('/')({
  head: () => ({
    meta: [
      { title: 'My App - Home' },
      { name: 'description', content: 'Welcome to My App' },
    ],
  }),
})

Dynamic Meta from Loader Data

tsx
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 },
    ],
  }),
})

Structured Data (JSON-LD)

tsx
head: ({ loaderData }) => ({
  scripts: [
    {
      type: 'application/ld+json',
      children: JSON.stringify({
        '@context': 'https://schema.org',
        '@type': 'Article',
        headline: loaderData.title,
      }),
    },
  ],
})

Dynamic Sitemap via Server Route

ts
// 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' },
        })
      },
    },
  },
})

Common Mistakes

1. HIGH: Missing nodejs_compat flag for Cloudflare Workers

jsonc
// WRONG — Node.js APIs fail at runtime
{ "compatibility_flags": [] }

// CORRECT
{ "compatibility_flags": ["nodejs_compat"] }

2. MEDIUM: Bun deployment with React 18

Bun-specific deployment only works with React 19. Use Node.js deployment for React 18.

3. MEDIUM: Child route loosening parent SSR config

tsx
// 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
})

Cross-References

같은 저장소의 Skills

더 많은 Skills

모든 Skills
tanstack
커뮤니티

react-start

- React bindings for TanStack Start: createStart, StartClient, StartServer, React-specific imports, re-exports from @tanstack/react-router, full project setup with React, useServerFn hook.

설치 수
5
GitHub Stars
1.5만
업데이트
10월 10일
tanstack
커뮤니티

auth-and-guards

- 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.

설치 수
4
GitHub Stars
1.5만
업데이트
10월 10일
tanstack
커뮤니티

auth-server-primitives

- 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.

설치 수
4
GitHub Stars
1.5만
업데이트
10월 10일
tanstack
커뮤니티

code-splitting

- Automatic code splitting (autoCodeSplitting), .lazy.tsx convention, createLazyFileRoute, createLazyRoute, lazyRouteComponent, getRouteApi for typed hooks in split files, codeSplitGroupings per-route override, splitBehavior programmatic config, critical vs non-critical properties.

설치 수
4
GitHub Stars
1.5만
업데이트
10월 10일