tanstack-start

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

TanStack Start

TanStack Start

Full-stack React framework powered by TanStack Router and Vite. Adds SSR, streaming, server functions, middleware, server routes, and universal deployment to TanStack Router's type-safe routing.
TanStack Start is in Release Candidate stage. API is stable and feature-complete. No RSC support yet (in active development).
基于TanStack Router和Vite的全栈React框架。为TanStack Router的类型安全路由添加了SSR、流式传输、服务器函数、中间件、服务器路由以及通用部署能力。
TanStack Start处于候选发布阶段。API已稳定且功能完整,目前暂不支持RSC(正在积极开发中)。

When to Use This Skill

何时使用该工具

  • Building full-stack React with SSR, SSG, or streaming
  • Adding server functions (type-safe RPCs) to a React app
  • Creating API/server routes alongside frontend routes
  • Implementing middleware for auth, logging, or request handling
  • Deploying to Cloudflare Workers, Netlify, Vercel, Node.js, Bun, or Docker
  • Need SPA mode with optional server functions (no SSR required)
Use TanStack Router alone (see tanstack-router skill) when you only need client-side routing without server features.
For routing concepts (file-based routing, search params, nested layouts, loaders, preloading), refer to the tanstack-router skill. This skill covers Start-specific full-stack features.
  • 构建支持SSR、SSG或流式传输的全栈React应用
  • 为React应用添加服务器函数(类型安全RPC)
  • 与前端路由一同创建API/服务器路由
  • 实现用于认证、日志或请求处理的中间件
  • 部署至Cloudflare Workers、Netlify、Vercel、Node.js、Bun或Docker环境
  • 需要带可选服务器函数的SPA模式(无需SSR)
仅使用TanStack Router(参考tanstack-router工具):当你只需要客户端路由,不需要服务器端功能时。
关于路由概念(基于文件的路由、搜索参数、嵌套布局、加载器、预加载),请参考tanstack-router工具。本工具仅涵盖Start特有的全栈功能。

Quick Start Workflow

快速开始流程

1. Create Project

1. 创建项目

bash
pnpm create @tanstack/start@latest
bash
pnpm create @tanstack/start@latest

2. Manual Setup

2. 手动搭建

bash
npm i @tanstack/react-start @tanstack/react-router react react-dom
npm i -D vite @vitejs/plugin-react typescript @types/react @types/react-dom vite-tsconfig-paths
bash
npm i @tanstack/react-start @tanstack/react-router react react-dom
npm i -D vite @vitejs/plugin-react typescript @types/react @types/react-dom vite-tsconfig-paths

3. Vite Configuration

3. Vite配置

ts
// vite.config.ts
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({
  server: { port: 3000 },
  plugins: [
    tsConfigPaths(),
    tanstackStart(),
    viteReact(), // MUST come after tanstackStart()
  ],
})
ts
// vite.config.ts
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({
  server: { port: 3000 },
  plugins: [
    tsConfigPaths(),
    tanstackStart(),
    viteReact(), // 必须在tanstackStart()之后
  ],
})

4. Router and Root Route

4. 路由与根路由

tsx
// src/router.tsx
import { createRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'

export function getRouter() {
  return createRouter({ routeTree, scrollRestoration: true })
}
tsx
// src/routes/__root.tsx
/// <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 TanStack Start App' },
    ],
  }),
  component: () => (
    <html>
      <head><HeadContent /></head>
      <body><Outlet /><Scripts /></body>
    </html>
  ),
})
tsx
// src/router.tsx
import { createRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'

export function getRouter() {
  return createRouter({ routeTree, scrollRestoration: true })
}
tsx
// src/routes/__root.tsx
/// <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 TanStack Start App' },
    ],
  }),
  component: () => (
    <html>
      <head><HeadContent /></head>
      <body><Outlet /><Scripts /></body>
    </html>
  ),
})

5. Route with Server Function

5. 带服务器函数的路由

tsx
// src/routes/index.tsx
import { createFileRoute } from '@tanstack/react-router'
import { createServerFn } from '@tanstack/react-start'

const getServerTime = createServerFn({ method: 'GET' }).handler(async () => {
  return new Date().toISOString()
})

export const Route = createFileRoute('/')({
  loader: () => getServerTime(),
  component: () => {
    const time = Route.useLoaderData()
    return <div>Server time: {time}</div>
  },
})
tsx
// src/routes/index.tsx
import { createFileRoute } from '@tanstack/react-router'
import { createServerFn } from '@tanstack/react-start'

const getServerTime = createServerFn({ method: 'GET' }).handler(async () => {
  return new Date().toISOString()
})

export const Route = createFileRoute('/')({
  loader: () => getServerTime(),
  component: () => {
    const time = Route.useLoaderData()
    return <div>Server time: {time}</div>
  },
})

File Structure

文件结构

src/
├── routes/
│   ├── __root.tsx        # HTML shell, always rendered
│   └── index.tsx
├── router.tsx            # Router config
├── routeTree.gen.ts      # Auto-generated
├── start.ts              # Optional: global middleware
└── server.ts             # Optional: custom server entry
src/
├── routes/
│   ├── __root.tsx        # HTML外壳,始终会渲染
│   └── index.tsx
├── router.tsx            # 路由配置
├── routeTree.gen.ts      # 自动生成
├── start.ts              # 可选:全局中间件
└── server.ts             # 可选:自定义服务器入口

Execution Model

执行模型

All code is isomorphic by default - runs in both server and client bundles unless constrained. Route
loader
s run on the server during SSR AND on the client during navigation.
tsx
// WRONG - secret exposed to client bundle
export const Route = createFileRoute('/users')({
  loader: () => {
    const secret = process.env.SECRET
    return fetch(`/api/users?key=${secret}`)
  },
})

// CORRECT - server function keeps secrets server-side
const getUsers = createServerFn().handler(async () => {
  return fetch(`/api/users?key=${process.env.SECRET}`)
})

export const Route = createFileRoute('/users')({
  loader: () => getUsers(),
})
APIRuns OnClient Behavior
createServerFn()
ServerNetwork request (RPC)
createServerOnlyFn(fn)
ServerThrows error
createClientOnlyFn(fn)
ClientWorks normally
createIsomorphicFn()
BothEnvironment-specific impl
<ClientOnly>
ClientRenders fallback on server
默认情况下,所有代码都是同构的——除非加以限制,否则会同时在服务器和客户端包中运行。路由的
loader
会在SSR期间在服务器上运行,也会在客户端导航时在客户端运行。
tsx
// 错误示例 - 密钥暴露给客户端包
export const Route = createFileRoute('/users')({
  loader: () => {
    const secret = process.env.SECRET
    return fetch(`/api/users?key=${secret}`)
  },
})

// 正确示例 - 服务器函数将密钥保留在服务器端
const getUsers = createServerFn().handler(async () => {
  return fetch(`/api/users?key=${process.env.SECRET}`)
})

export const Route = createFileRoute('/users')({
  loader: () => getUsers(),
})
API运行环境客户端行为
createServerFn()
服务器网络请求(RPC)
createServerOnlyFn(fn)
服务器抛出错误
createClientOnlyFn(fn)
客户端正常运行
createIsomorphicFn()
两者基于环境的特定实现
<ClientOnly>
客户端在服务器上渲染回退内容

Server Functions

服务器函数

Type-safe RPCs via
createServerFn()
. Server code is extracted from client bundles at build time; client calls become
fetch
requests.
tsx
import { createServerFn } from '@tanstack/react-start'
import { z } from 'zod'
import { redirect, notFound } from '@tanstack/react-router'

// GET with no input
export const getData = createServerFn({ method: 'GET' }).handler(async () => {
  return { message: 'Hello from server!' }
})

// POST with Zod validation
const CreatePostSchema = z.object({
  title: z.string().min(1).max(200),
  body: z.string().min(1),
})

export const createPost = createServerFn({ method: 'POST' })
  .inputValidator(CreatePostSchema)
  .handler(async ({ data }) => {
    return await db.posts.create(data)
  })

// Redirect and notFound
export const getPost = createServerFn()
  .inputValidator((data: { id: string }) => data)
  .handler(async ({ data }) => {
    const post = await db.findPost(data.id)
    if (!post) throw notFound()
    return post
  })
通过
createServerFn()
实现类型安全的RPC。构建时服务器代码会从客户端包中提取;客户端调用会变为
fetch
请求。
tsx
import { createServerFn } from '@tanstack/react-start'
import { z } from 'zod'
import { redirect, notFound } from '@tanstack/react-router'

// 无输入的GET请求
export const getData = createServerFn({ method: 'GET' }).handler(async () => {
  return { message: 'Hello from server!' }
})

// 带Zod验证的POST请求
const CreatePostSchema = z.object({
  title: z.string().min(1).max(200),
  body: z.string().min(1),
})

export const createPost = createServerFn({ method: 'POST' })
  .inputValidator(CreatePostSchema)
  .handler(async ({ data }) => {
    return await db.posts.create(data)
  })

// 重定向与404处理
export const getPost = createServerFn()
  .inputValidator((data: { id: string }) => data)
  .handler(async ({ data }) => {
    const post = await db.findPost(data.id)
    if (!post) throw notFound()
    return post
  })

Calling Server Functions

调用服务器函数

tsx
// From loader
export const Route = createFileRoute('/posts')({
  loader: () => getPosts(),
})

// From component with useServerFn
import { useServerFn } from '@tanstack/react-start'

function CreatePostForm() {
  const mutation = useServerFn(createPost)
  return <button onClick={() => mutation({ data: { title: 'New', body: 'Content' } })}>Create</button>
}

// Direct call with router.invalidate()
function DeleteButton({ id }: { id: string }) {
  const router = useRouter()
  return <button onClick={() => deletePost({ data: { id } }).then(() => router.invalidate())}>Delete</button>
}
tsx
// 从loader中调用
export const Route = createFileRoute('/posts')({
  loader: () => getPosts(),
})

// 在组件中使用useServerFn调用
import { useServerFn } from '@tanstack/react-start'

function CreatePostForm() {
  const mutation = useServerFn(createPost)
  return <button onClick={() => mutation({ data: { title: 'New', body: 'Content' } })}>Create</button>
}

// 直接调用并结合router.invalidate()
function DeleteButton({ id }: { id: string }) {
  const router = useRouter()
  return <button onClick={() => deletePost({ data: { id } }).then(() => router.invalidate())}>Delete</button>
}

Server Context Utilities

服务器上下文工具

Access request/response from
@tanstack/react-start/server
:
getRequest()
,
getRequestHeader(name)
,
setResponseHeaders(headers)
,
setResponseStatus(code)
.
@tanstack/react-start/server
访问请求/响应:
getRequest()
getRequestHeader(name)
setResponseHeaders(headers)
setResponseStatus(code)

Middleware

中间件

Two types: request middleware (all server requests including SSR) and server function middleware (server functions only, with client-side hooks and input validation).
分为两种类型:请求中间件(包括SSR在内的所有服务器请求)和服务器函数中间件(仅适用于服务器函数,带有客户端钩子和输入验证)。

Request Middleware

请求中间件

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

const loggingMiddleware = createMiddleware().server(async ({ next, request }) => {
  const start = Date.now()
  const result = await next()
  console.log(`${request.method} ${request.url} - ${Date.now() - start}ms`)
  return result
})
tsx
import { createMiddleware } from '@tanstack/react-start'

const loggingMiddleware = createMiddleware().server(async ({ next, request }) => {
  const start = Date.now()
  const result = await next()
  console.log(`${request.method} ${request.url} - ${Date.now() - start}ms`)
  return result
})

Server Function Middleware with Context

带上下文的服务器函数中间件

tsx
const authMiddleware = createMiddleware({ type: 'function' })
  .server(async ({ next }) => {
    const user = await getCurrentUser()
    if (!user) throw redirect({ to: '/login' })
    return next({ context: { user } })
  })

const getProfile = createServerFn()
  .middleware([authMiddleware])
  .handler(async ({ context }) => {
    return context.user // typed
  })
tsx
const authMiddleware = createMiddleware({ type: 'function' })
  .server(async ({ next }) => {
    const user = await getCurrentUser()
    if (!user) throw redirect({ to: '/login' })
    return next({ context: { user } })
  })

const getProfile = createServerFn()
  .middleware([authMiddleware])
  .handler(async ({ context }) => {
    return context.user // 已类型化
  })

Client + Server Middleware

客户端+服务器中间件

tsx
const authHeaderMiddleware = createMiddleware({ type: 'function' })
  .client(async ({ next }) => {
    return next({ headers: { Authorization: `Bearer ${getToken()}` } })
  })
  .server(async ({ next }) => {
    const user = await verifyToken(getRequestHeader('Authorization'))
    return next({ context: { user } })
  })
tsx
const authHeaderMiddleware = createMiddleware({ type: 'function' })
  .client(async ({ next }) => {
    return next({ headers: { Authorization: `Bearer ${getToken()}` } })
  })
  .server(async ({ next }) => {
    const user = await verifyToken(getRequestHeader('Authorization'))
    return next({ context: { user } })
  })

Global Middleware (src/start.ts)

全局中间件(src/start.ts)

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

export const startInstance = createStart(() => ({
  requestMiddleware: [globalLogger],  // ALL requests (SSR, routes, fns)
  functionMiddleware: [globalAuth],   // ALL server functions
}))
tsx
import { createStart, createMiddleware } from '@tanstack/react-start'

export const startInstance = createStart(() => ({
  requestMiddleware: [globalLogger],  // 所有请求(SSR、路由、函数)
  functionMiddleware: [globalAuth],   // 所有服务器函数
}))

Server Routes

服务器路由

HTTP endpoints alongside frontend routes using file-based routing. Handlers receive
{ request, params, context }
and return
Response
.
tsx
// src/routes/api/users.ts
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/api/users')({
  server: {
    middleware: [authMiddleware],
    handlers: {
      GET: async ({ request }) => {
        return Response.json(await db.users.findMany())
      },
      POST: async ({ request }) => {
        const body = await request.json()
        return Response.json(await db.users.create(body), { status: 201 })
      },
    },
  },
})
Per-handler middleware via
createHandlers
:
tsx
server: {
  handlers: ({ createHandlers }) => createHandlers({
    GET: async ({ request }) => Response.json({ ok: true }),
    DELETE: {
      middleware: [adminOnlyMiddleware],
      handler: async ({ request }) => Response.json({ deleted: true }),
    },
  }),
}
Server routes and components can co-exist in the same file. Dynamic params (
$id
), wildcards (
$
), and escaped matching (
[.]json
) all work identically to Router.
通过基于文件的路由,在前端路由旁创建HTTP端点。处理器接收
{ request, params, context }
并返回
Response
tsx
// src/routes/api/users.ts
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/api/users')({
  server: {
    middleware: [authMiddleware],
    handlers: {
      GET: async ({ request }) => {
        return Response.json(await db.users.findMany())
      },
      POST: async ({ request }) => {
        const body = await request.json()
        return Response.json(await db.users.create(body), { status: 201 })
      },
    },
  },
})
通过
createHandlers
为单个处理器添加中间件:
tsx
server: {
  handlers: ({ createHandlers }) => createHandlers({
    GET: async ({ request }) => Response.json({ ok: true }),
    DELETE: {
      middleware: [adminOnlyMiddleware],
      handler: async ({ request }) => Response.json({ deleted: true }),
    },
  }),
}
服务器路由和组件可以共存于同一文件中。动态参数(
$id
)、通配符(
$
)和转义匹配(
[.]json
)的工作方式与Router完全相同。

SSR Modes

SSR模式

Per-route SSR control via the
ssr
property:
ModeLoadersComponentUse Case
true
(default)
Server + ClientServer + ClientSEO, performance
false
Client onlyClient onlyBrowser APIs, canvas
'data-only'
Server + ClientClient onlyDashboards
(params, search) => ...
DynamicDynamicConditional SSR
tsx
export const Route = createFileRoute('/dashboard')({
  ssr: 'data-only',
  loader: () => getDashboardData(),
  component: Dashboard,
})
通过
ssr
属性按路由控制SSR:
模式加载器组件使用场景
true
(默认)
服务器+客户端服务器+客户端SEO、性能优化
false
仅客户端仅客户端浏览器API、画布应用
'data-only'
服务器+客户端仅客户端仪表盘应用
(params, search) => ...
动态动态条件式SSR
tsx
export const Route = createFileRoute('/dashboard')({
  ssr: 'data-only',
  loader: () => getDashboardData(),
  component: Dashboard,
})

SPA Mode

SPA模式

Ship static HTML shells with server function support but no SSR:
ts
// vite.config.ts
tanstackStart({ spa: { enabled: true } })
交付静态HTML外壳,支持服务器函数但无SSR:
ts
// vite.config.ts
tanstackStart({ spa: { enabled: true } })

Global Default

全局默认设置

tsx
// src/start.ts
export const startInstance = createStart(() => ({ defaultSsr: false }))
tsx
// src/start.ts
export const startInstance = createStart(() => ({ defaultSsr: false }))

Head Management and SEO

头部管理与SEO

tsx
export const Route = createFileRoute('/posts/$postId')({
  loader: async ({ params }) => ({ post: await getPost({ data: { id: params.postId } }) }),
  head: ({ loaderData }) => ({
    meta: [
      { title: loaderData.post.title },
      { name: 'description', content: loaderData.post.excerpt },
      { property: 'og:title', content: loaderData.post.title },
      { property: 'og:image', content: loaderData.post.coverImage },
      { name: 'twitter:card', content: 'summary_large_image' },
    ],
    links: [{ rel: 'canonical', href: `https://myapp.com/posts/${loaderData.post.id}` }],
  }),
  component: PostPage,
})
tsx
export const Route = createFileRoute('/posts/$postId')({
  loader: async ({ params }) => ({ post: await getPost({ data: { id: params.postId } }) }),
  head: ({ loaderData }) => ({
    meta: [
      { title: loaderData.post.title },
      { name: 'description', content: loaderData.post.excerpt },
      { property: 'og:title', content: loaderData.post.title },
      { property: 'og:image', content: loaderData.post.coverImage },
      { name: 'twitter:card', content: 'summary_large_image' },
    ],
    links: [{ rel: 'canonical', href: `https://myapp.com/posts/${loaderData.post.id}` }],
  }),
  component: PostPage,
})

Authentication

认证

Session Management

会话管理

tsx
// utils/session.ts
import { useSession } from '@tanstack/react-start/server'

export function useAppSession() {
  return useSession<{ userId?: string; email?: string }>({
    name: 'app-session',
    password: process.env.SESSION_SECRET!,
    cookie: { secure: process.env.NODE_ENV === 'production', sameSite: 'lax', httpOnly: true },
  })
}
tsx
// utils/session.ts
import { useSession } from '@tanstack/react-start/server'

export function useAppSession() {
  return useSession<{ userId?: string; email?: string }>({
    name: 'app-session',
    password: process.env.SESSION_SECRET!,
    cookie: { secure: process.env.NODE_ENV === 'production', sameSite: 'lax', httpOnly: true },
  })
}

Route Protection

路由保护

tsx
// src/routes/_authed.tsx - layout route guard
export const Route = createFileRoute('/_authed')({
  beforeLoad: async ({ location }) => {
    const user = await getCurrentUserFn()
    if (!user) throw redirect({ to: '/login', search: { redirect: location.href } })
    return { user }
  },
})

// src/routes/_authed/dashboard.tsx - automatically protected
export const Route = createFileRoute('/_authed/dashboard')({
  component: () => {
    const { user } = Route.useRouteContext()
    return <h1>Welcome, {user.email}!</h1>
  },
})
tsx
// src/routes/_authed.tsx - 布局路由守卫
export const Route = createFileRoute('/_authed')({
  beforeLoad: async ({ location }) => {
    const user = await getCurrentUserFn()
    if (!user) throw redirect({ to: '/login', search: { redirect: location.href } })
    return { user }
  },
})

// src/routes/_authed/dashboard.tsx - 自动受保护
export const Route = createFileRoute('/_authed/dashboard')({
  component: () => {
    const { user } = Route.useRouteContext()
    return <h1>Welcome, {user.email}!</h1>
  },
})

Environment Functions

环境函数

tsx
import { createIsomorphicFn, createServerOnlyFn, createClientOnlyFn } from '@tanstack/react-start'
import { ClientOnly } from '@tanstack/react-router'

const getDeviceInfo = createIsomorphicFn()
  .server(() => ({ type: 'server', platform: process.platform }))
  .client(() => ({ type: 'client', userAgent: navigator.userAgent }))

const getDbUrl = createServerOnlyFn(() => process.env.DATABASE_URL) // throws on client
const saveLocal = createClientOnlyFn((k: string, v: string) => localStorage.setItem(k, v)) // throws on server

// Component-level: renders fallback during SSR, children after hydration
<ClientOnly fallback={<div>Loading...</div>}><InteractiveChart /></ClientOnly>
tsx
import { createIsomorphicFn, createServerOnlyFn, createClientOnlyFn } from '@tanstack/react-start'
import { ClientOnly } from '@tanstack/react-router'

const getDeviceInfo = createIsomorphicFn()
  .server(() => ({ type: 'server', platform: process.platform }))
  .client(() => ({ type: 'client', userAgent: navigator.userAgent }))

const getDbUrl = createServerOnlyFn(() => process.env.DATABASE_URL) // 在客户端会抛出错误
const saveLocal = createClientOnlyFn((k: string, v: string) => localStorage.setItem(k, v)) // 在服务器会抛出错误

// 组件层面:SSR期间渲染回退内容, hydration后渲染子组件
<ClientOnly fallback={<div>Loading...</div>}><InteractiveChart /></ClientOnly>

Deployment

部署

Cloudflare Workers (Official Partner)

Cloudflare Workers(官方合作伙伴)

Install
@cloudflare/vite-plugin
and
wrangler
, add
cloudflare({ viteEnvironment: { name: 'ssr' } })
to vite plugins (before
tanstackStart()
), and set
"main": "@tanstack/react-start/server-entry"
in
wrangler.jsonc
.
安装
@cloudflare/vite-plugin
wrangler
,在vite插件中添加
cloudflare({ viteEnvironment: { name: 'ssr' } })
(在
tanstackStart()
之前),并在
wrangler.jsonc
中设置
"main": "@tanstack/react-start/server-entry"

Netlify (Official Partner)

Netlify(官方合作伙伴)

Install
@netlify/vite-plugin-tanstack-start
, add
netlify()
to vite plugins alongside
tanstackStart()
.
安装
@netlify/vite-plugin-tanstack-start
,在vite插件中添加
netlify()
tanstackStart()
一同使用。

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

Nitro(Node.js、Vercel、Bun、Docker)

Install
nitro@npm:nitro-nightly@latest
, add
nitro()
to vite plugins. Build with
vite build
, run with
node .output/server/index.mjs
.
安装
nitro@npm:nitro-nightly@latest
,在vite插件中添加
nitro()
。使用
vite build
构建,
node .output/server/index.mjs
运行。

Static Prerendering

静态预渲染

ts
tanstackStart({ prerender: { enabled: true, crawlLinks: true } })
ts
tanstackStart({ prerender: { enabled: true, crawlLinks: true } })

Best Practices

最佳实践

  1. Never put secrets in loaders - Loaders are isomorphic. Use
    createServerFn()
    for server-only access.
  2. Server functions are the boundary - Primary mechanism for safe server-only execution from client code.
  3. Organize by concern -
    .functions.ts
    for server fn wrappers,
    .server.ts
    for internal helpers,
    .ts
    for shared types/schemas.
  4. Compose middleware hierarchically - Global for cross-cutting concerns, route-level for groups, function-level for specifics.
  5. Use
    head()
    on every content route
    - Title, description, OG tags. Use loader data for dynamic pages.
  6. Choose SSR mode per route -
    true
    for SEO,
    false
    for browser-only,
    'data-only'
    for dashboards.
  7. Validate all server function inputs - Zod or custom validators via
    .inputValidator()
    .
  1. 切勿在loader中存放密钥 - Loader是同构的。使用
    createServerFn()
    实现仅服务器端访问。
  2. 服务器函数是安全边界 - 从客户端代码安全执行仅服务器端逻辑的主要机制。
  3. 按关注点组织代码 -
    .functions.ts
    用于服务器函数封装,
    .server.ts
    用于内部工具,
    .ts
    用于共享类型/模式。
  4. 分层组合中间件 - 全局中间件用于跨领域关注点,路由级用于分组,函数级用于特定场景。
  5. 在每个内容路由中使用
    head()
    - 设置标题、描述、OG标签。使用loader数据处理动态页面。
  6. 为每个路由选择合适的SSR模式 -
    true
    用于SEO,
    false
    用于仅浏览器应用,
    'data-only'
    用于仪表盘。
  7. 验证所有服务器函数输入 - 通过
    .inputValidator()
    使用Zod或自定义验证器。

Advanced Topics

进阶主题

For deeper coverage, see reference files:
  • references/server-functions.md
    - Streaming, FormData, progressive enhancement, request cancellation, custom function IDs
  • references/middleware.md
    - sendContext, custom fetch, global config, environment tree shaking
  • references/ssr-modes.md
    - Selective SSR inheritance, functional form, shellComponent, fallback rendering
  • references/server-routes.md
    - Dynamic params, wildcards, escaped matching, pathless layouts
如需深入了解,请参考以下参考文件:
  • references/server-functions.md
    - 流式传输、FormData、渐进式增强、请求取消、自定义函数ID
  • references/middleware.md
    - sendContext、自定义fetch、全局配置、环境树摇
  • references/ssr-modes.md
    - 选择性SSR继承、函数形式、shellComponent、回退渲染
  • references/server-routes.md
    - 动态参数、通配符、转义匹配、无路径布局

Resources

资源

  • Official Docs
  • GitHub (Start lives in the router repo)
  • Examples - Basic, Auth, React Query, Cloudflare, Clerk, Supabase
  • Start vs Next.js
  • Cross-reference: tanstack-router skill for routing, tanstack-query skill for data fetching
  • 官方文档
  • GitHub(Start位于router仓库中)
  • 示例 - 基础示例、认证、React Query、Cloudflare、Clerk、Supabase
  • Start vs Next.js
  • 交叉参考:tanstack-router工具用于路由,tanstack-query工具用于数据获取