tanstack-start

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

TanStack Start

TanStack Start

Full-stack React framework with SSR, server functions, and Vite bundling.
基于SSR、服务端函数和Vite打包的全栈React框架。

Project Setup

项目搭建

New project:
bash
pnpm create cloudflare@latest my-app --framework=tanstack-start -y --no-deploy
Existing app: See references/migration.md for converting React/Vite apps.
新项目:
bash
pnpm create cloudflare@latest my-app --framework=tanstack-start -y --no-deploy
现有项目: 如需将React/Vite项目迁移,请查看references/migration.md

Critical Configuration

关键配置

vite.config.ts

vite.config.ts

typescript
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
  ],
})
typescript
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(),  // 必须在tanstackStart之后
  ],
})

tsconfig.json gotcha

tsconfig.json 注意事项

Do NOT enable
verbatimModuleSyntax
- it leaks server bundles into client bundles.
请勿启用
verbatimModuleSyntax
——它会导致服务端包泄露到客户端包中。

Server Functions

服务端函数

See references/server-functions.md for complete guide.
⚠️ Critical: Route loaders run on server for initial SSR, but run on CLIENT during navigation. Always wrap server code in
createServerFn()
to ensure it runs server-side.
When to use what (Cloudflare Workers):
Use CaseSolution
Server code in route loaders
createServerFn()
Server code from client event handlersAPI routes (
server.handlers
) work best
Access Cloudflare bindings
import { env } from 'cloudflare:workers'
createServerFn - for loaders:
typescript
import { createServerFn } from '@tanstack/react-start'

export const getData = createServerFn().handler(async () => {
  return { data: process.env.SECRET }  // Server-only
})
API routes - for client event handlers:
tsx
// routes/api/users.ts
export const Route = createFileRoute('/api/users')({
  server: {
    handlers: {
      POST: async ({ request }) => {
        const body = await request.json()
        return Response.json(await db.users.create(body))
      },
    },
  },
})

// In component: fetch('/api/users', { method: 'POST', body: JSON.stringify(data) })
Key APIs:
  • createServerFn()
    - Server-only functions for loaders
  • server.handlers
    - API routes for client event handlers
  • createMiddleware({ type: 'function' })
    - Reusable middleware
  • @tanstack/react-start/server
    :
    getRequestHeaders()
    ,
    setResponseHeader()
    ,
    getCookies()
完整指南请查看references/server-functions.md
⚠️ 重要提示:路由加载器在初始SSR时运行在服务端,但在导航过程中运行在客户端。请始终将服务端代码包裹在
createServerFn()
中,以确保其在服务端运行。
使用场景对应方案(Cloudflare Workers):
使用场景解决方案
路由加载器中的服务端代码
createServerFn()
客户端事件处理器调用的服务端代码API路由(
server.handlers
)是最佳选择
访问Cloudflare绑定
import { env } from 'cloudflare:workers'
createServerFn - 用于加载器:
typescript
import { createServerFn } from '@tanstack/react-start'

export const getData = createServerFn().handler(async () => {
  return { data: process.env.SECRET }  // 仅服务端可用
})
API路由 - 用于客户端事件处理器:
tsx
// routes/api/users.ts
export const Route = createFileRoute('/api/users')({
  server: {
    handlers: {
      POST: async ({ request }) => {
        const body = await request.json()
        return Response.json(await db.users.create(body))
      },
    },
  },
})

// 在组件中调用: fetch('/api/users', { method: 'POST', body: JSON.stringify(data) })
核心API:
  • createServerFn()
    - 用于加载器的仅服务端函数
  • server.handlers
    - 供客户端事件处理器调用的API路由
  • createMiddleware({ type: 'function' })
    - 可复用中间件
  • @tanstack/react-start/server
    :
    getRequestHeaders()
    ,
    setResponseHeader()
    ,
    getCookies()

Routing

路由

See references/routing.md for complete patterns.
File conventions in
src/routes/
:
PatternRoute
index.tsx
/
posts.$postId.tsx
/posts/:postId
_layout.tsx
Layout (no URL)
__root.tsx
Root layout (required)
tsx
import { createFileRoute } from '@tanstack/react-router'
import { createServerFn } from '@tanstack/react-start'

const getPost = createServerFn()
  .handler(async () => await db.post.findFirst())

export const Route = createFileRoute('/posts/$postId')({
  loader: ({ params }) => getPost({ data: params.postId }),
  component: () => {
    const post = Route.useLoaderData()
    return <h1>{post.title}</h1>
  },
})
完整路由模式请查看references/routing.md
src/routes/
目录下的文件约定:
模式路由
index.tsx
/
posts.$postId.tsx
/posts/:postId
_layout.tsx
布局(无对应URL)
__root.tsx
根布局(必填)
tsx
import { createFileRoute } from '@tanstack/react-router'
import { createServerFn } from '@tanstack/react-start'

const getPost = createServerFn()
  .handler(async () => await db.post.findFirst())

export const Route = createFileRoute('/posts/$postId')({
  loader: ({ params }) => getPost({ data: params.postId }),
  component: () => {
    const post = Route.useLoaderData()
    return <h1>{post.title}</h1>
  },
})

Cloudflare Deployment

Cloudflare部署

See references/cloudflare-deployment.md for complete guide.
bash
pnpm add -D @cloudflare/vite-plugin wrangler
vite.config.ts - add cloudflare plugin:
typescript
import { cloudflare } from '@cloudflare/vite-plugin'
// Add to plugins: cloudflare({ viteEnvironment: { name: 'ssr' } })
wrangler.jsonc:
jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "name": "my-app",
  "compatibility_date": "<CURRENT_DATE>",  // Use today's YYYY-MM-DD
  "compatibility_flags": ["nodejs_compat"],
  "main": "@tanstack/react-start/server-entry",
  "observability": { "enabled": true }
}
Access Cloudflare bindings in server functions:
typescript
import { env } from 'cloudflare:workers'
const value = await env.MY_KV.get('key')
Static Prerendering (v1.138.0+):
typescript
tanstackStart({ prerender: { enabled: true } })
完整部署指南请查看references/cloudflare-deployment.md
bash
pnpm add -D @cloudflare/vite-plugin wrangler
vite.config.ts - 添加cloudflare插件:
typescript
import { cloudflare } from '@cloudflare/vite-plugin'
// 添加到plugins中: cloudflare({ viteEnvironment: { name: 'ssr' } })
wrangler.jsonc:
jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "name": "my-app",
  "compatibility_date": "<CURRENT_DATE>",  // 使用当前日期,格式为YYYY-MM-DD
  "compatibility_flags": ["nodejs_compat"],
  "main": "@tanstack/react-start/server-entry",
  "observability": { "enabled": true }
}
在服务端函数中访问Cloudflare绑定:
typescript
import { env } from 'cloudflare:workers'
const value = await env.MY_KV.get('key')
静态预渲染(v1.138.0及以上版本):
typescript
tanstackStart({ prerender: { enabled: true } })

TanStack Query Integration

TanStack Query集成

See references/query-integration.md for SSR setup.
bash
pnpm add @tanstack/react-query @tanstack/react-router-ssr-query
tsx
// Preload in loaders, consume with useSuspenseQuery
loader: ({ context }) => context.queryClient.ensureQueryData(myQueryOptions)
SSR设置请查看references/query-integration.md
bash
pnpm add @tanstack/react-query @tanstack/react-router-ssr-query
tsx
// 在加载器中预加载,使用useSuspenseQuery消费数据
loader: ({ context }) => context.queryClient.ensureQueryData(myQueryOptions)

Better-Auth Integration

Better-Auth集成

See references/better-auth.md for complete guide.
⚠️ Critical: Use
createFileRoute
with
server.handlers
, NOT the legacy
createAPIFileRoute
.
Mount the auth handler at
/src/routes/api/auth/$.ts
:
typescript
import { auth } from '@/lib/auth'
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/api/auth/$')({
  server: {
    handlers: {
      GET: ({ request }) => auth.handler(request),
      POST: ({ request }) => auth.handler(request),
    },
  },
})
Auth config with
tanstackStartCookies
plugin:
typescript
import { betterAuth } from "better-auth"
import { tanstackStartCookies } from "better-auth/tanstack-start"

export const auth = betterAuth({
  // ...your config
  plugins: [tanstackStartCookies()] // MUST be last plugin
})
Protect routes with middleware:
typescript
import { createMiddleware } from "@tanstack/react-start"
import { getRequestHeaders } from "@tanstack/react-start/server"
import { auth } from "./auth"

export const authMiddleware = createMiddleware().server(
  async ({ next }) => {
    const session = await auth.api.getSession({ headers: getRequestHeaders() })
    if (!session) throw redirect({ to: "/login" })
    return next()
  }
)

// In route:
export const Route = createFileRoute('/dashboard')({
  server: { middleware: [authMiddleware] },
  component: Dashboard,
})
完整指南请查看references/better-auth.md
⚠️ 重要提示:请使用
createFileRoute
搭配
server.handlers
,而非旧版的
createAPIFileRoute
/src/routes/api/auth/$.ts
挂载认证处理器:
typescript
import { auth } from '@/lib/auth'
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/api/auth/$')({
  server: {
    handlers: {
      GET: ({ request }) => auth.handler(request),
      POST: ({ request }) => auth.handler(request),
    },
  },
})
搭配
tanstackStartCookies
插件的认证配置:
typescript
import { betterAuth } from "better-auth"
import { tanstackStartCookies } from "better-auth/tanstack-start"

export const auth = betterAuth({
  // ...你的配置
  plugins: [tanstackStartCookies()] // 必须是最后一个插件
})
使用中间件保护路由:
typescript
import { createMiddleware } from "@tanstack/react-start"
import { getRequestHeaders } from "@tanstack/react-start/server"
import { auth } from "./auth"

export const authMiddleware = createMiddleware().server(
  async ({ next }) => {
    const session = await auth.api.getSession({ headers: getRequestHeaders() })
    if (!session) throw redirect({ to: "/login" })
    return next()
  }
)

// 在路由中使用:
export const Route = createFileRoute('/dashboard')({
  server: { middleware: [authMiddleware] },
  component: Dashboard,
})

GitHub Actions Auto-Deploy

GitHub Actions自动部署

See references/github-actions-deploy.md for CI/CD setup with preview deployments.
如需设置带预览部署的CI/CD,请查看references/github-actions-deploy.md