tanstack-start

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

TanStack Start Skills

TanStack Start 技能

Overview

概述

TanStack Start is a full-stack React framework built on TanStack Router, powered by Vite and Nitro (via Vinxi). It provides server-side rendering, streaming, server functions (RPC), middleware, API routes, and deploys to any platform via Nitro presets.
Package:
@tanstack/react-start
Router Plugin:
@tanstack/router-plugin
Build Tool: Vinxi (Vite + Nitro) Status: RC (Release Candidate) RSC Support: React Server Components support is in active development and will land as a non-breaking v1.x addition
TanStack Start是基于TanStack Router构建的全栈React框架,由Vite和Nitro(通过Vinxi)提供支持。它提供服务端渲染(SSR)、流式渲染、服务器函数(RPC)、中间件、API路由,并可通过Nitro预设部署至任意平台。
包:
@tanstack/react-start
路由插件:
@tanstack/router-plugin
构建工具: Vinxi(Vite + Nitro) 状态: RC(候选发布版) RSC支持: React Server Components支持正在积极开发中,将作为非破坏性更新加入v1.x版本

Installation & Project Setup

安装与项目设置

bash
npx @tanstack/cli create my-app
bash
npx @tanstack/cli create my-app

Or manually:

或手动安装:

npm install @tanstack/react-start @tanstack/react-router react react-dom npm install -D @tanstack/router-plugin typescript vite vite-tsconfig-paths
undefined
npm install @tanstack/react-start @tanstack/react-router react react-dom npm install -D @tanstack/router-plugin typescript vite vite-tsconfig-paths
undefined

Project Structure

项目结构

my-app/
  app/
    routes/
      __root.tsx          # Root layout
      index.tsx           # / route
      posts.$postId.tsx   # /posts/:postId
      api/
        users.ts          # /api/users API route
    client.tsx            # Client entry
    router.tsx            # Router creation
    ssr.tsx               # SSR entry
    routeTree.gen.ts      # Auto-generated route tree
  app.config.ts           # TanStack Start config
  tsconfig.json
  package.json
my-app/
  app/
    routes/
      __root.tsx          # 根布局
      index.tsx           # / 路由
      posts.$postId.tsx   # /posts/:postId 路由
      api/
        users.ts          # /api/users API路由
    client.tsx            # 客户端入口
    router.tsx            # 路由创建文件
    ssr.tsx               # SSR入口
    routeTree.gen.ts      # 自动生成的路由树
  app.config.ts           # TanStack Start配置文件
  tsconfig.json
  package.json

Configuration (
app.config.ts
)

配置(
app.config.ts

typescript
import { defineConfig } from '@tanstack/react-start/config'
import viteTsConfigPaths from 'vite-tsconfig-paths'

export default defineConfig({
  vite: {
    plugins: [
      viteTsConfigPaths({ projects: ['./tsconfig.json'] }),
    ],
  },
  server: {
    preset: 'node-server', // 'vercel' | 'netlify' | 'cloudflare-pages' | etc.
  },
  tsr: {
    appDirectory: './app',
    routesDirectory: './app/routes',
    generatedRouteTree: './app/routeTree.gen.ts',
  },
})
typescript
import { defineConfig } from '@tanstack/react-start/config'
import viteTsConfigPaths from 'vite-tsconfig-paths'

export default defineConfig({
  vite: {
    plugins: [
      viteTsConfigPaths({ projects: ['./tsconfig.json'] }),
    ],
  },
  server: {
    preset: 'node-server', // 'vercel' | 'netlify' | 'cloudflare-pages' | 其他
  },
  tsr: {
    appDirectory: './app',
    routesDirectory: './app/routes',
    generatedRouteTree: './app/routeTree.gen.ts',
  },
})

Server Functions (
createServerFn
)

服务器函数(
createServerFn

Server functions provide type-safe RPC calls between client and server.
服务器函数支持客户端与服务器之间的类型安全RPC调用。

Basic Server Functions

基础服务器函数

typescript
import { createServerFn } from '@tanstack/react-start'

// GET (data fetching, cacheable)
const getUsers = createServerFn()
  .handler(async () => {
    const users = await db.query.users.findMany()
    return users
  })

// POST (mutations, side effects)
const createUser = createServerFn({ method: 'POST' })
  .validator((data: { name: string; email: string }) => data)
  .handler(async ({ data }) => {
    const user = await db.insert(users).values(data).returning()
    return user
  })
typescript
import { createServerFn } from '@tanstack/react-start'

// GET(数据获取,可缓存)
const getUsers = createServerFn()
  .handler(async () => {
    const users = await db.query.users.findMany()
    return users
  })

// POST(数据变更,副作用操作)
const createUser = createServerFn({ method: 'POST' })
  .validator((data: { name: string; email: string }) => data)
  .handler(async ({ data }) => {
    const user = await db.insert(users).values(data).returning()
    return user
  })

With Zod Validation

结合Zod验证

typescript
import { z } from 'zod'

const updateUser = createServerFn({ method: 'POST' })
  .validator(
    z.object({
      id: z.string(),
      name: z.string().min(1),
      email: z.string().email(),
    })
  )
  .handler(async ({ data }) => {
    // data is fully typed: { id: string; name: string; email: string }
    return await db.update(users).set(data).where(eq(users.id, data.id))
  })
typescript
import { z } from 'zod'

const updateUser = createServerFn({ method: 'POST' })
  .validator(
    z.object({
      id: z.string(),
      name: z.string().min(1),
      email: z.string().email(),
    })
  )
  .handler(async ({ data }) => {
    // data类型已完全定义:{ id: string; name: string; email: string }
    return await db.update(users).set(data).where(eq(users.id, data.id))
  })

Middleware

中间件

Creating Middleware

创建中间件

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

const loggingMiddleware = createMiddleware().handler(async ({ next }) => {
  console.log('Request started')
  const result = await next()
  console.log('Request completed')
  return result
})
typescript
import { createMiddleware } from '@tanstack/react-start'

const loggingMiddleware = createMiddleware().handler(async ({ next }) => {
  console.log('请求开始')
  const result = await next()
  console.log('请求完成')
  return result
})

Auth Middleware with Context

带上下文的认证中间件

typescript
const authMiddleware = createMiddleware().handler(async ({ next }) => {
  const request = getWebRequest()
  const session = await getSession(request)

  if (!session?.user) {
    throw redirect({ to: '/login' })
  }

  // Pass typed context to handler
  return next({ context: { user: session.user } })
})
typescript
const authMiddleware = createMiddleware().handler(async ({ next }) => {
  const request = getWebRequest()
  const session = await getSession(request)

  if (!session?.user) {
    throw redirect({ to: '/login' })
  }

  // 将类型化上下文传递给处理器
  return next({ context: { user: session.user } })
})

Chaining Middleware

链式中间件

typescript
const adminMiddleware = createMiddleware()
  .middleware([authMiddleware])
  .handler(async ({ next, context }) => {
    // context.user is typed from authMiddleware
    if (context.user.role !== 'admin') {
      throw redirect({ to: '/unauthorized' })
    }
    return next({ context: { isAdmin: true } })
  })

// Usage
const adminAction = createServerFn({ method: 'POST' })
  .middleware([adminMiddleware])
  .handler(async ({ context }) => {
    // context: { user: User; isAdmin: boolean }
    return { success: true }
  })
typescript
const adminMiddleware = createMiddleware()
  .middleware([authMiddleware])
  .handler(async ({ next, context }) => {
    // context.user 继承自authMiddleware的类型
    if (context.user.role !== 'admin') {
      throw redirect({ to: '/unauthorized' })
    }
    return next({ context: { isAdmin: true } })
  })

// 使用示例
const adminAction = createServerFn({ method: 'POST' })
  .middleware([adminMiddleware])
  .handler(async ({ context }) => {
    // context: { user: User; isAdmin: boolean }
    return { success: true }
  })

API Routes (Server Routes)

API路由(服务器路由)

typescript
// app/routes/api/users.ts
import { createAPIFileRoute } from '@tanstack/react-start/api'

export const APIRoute = createAPIFileRoute('/api/users')({
  GET: async ({ request }) => {
    const users = await db.query.users.findMany()
    return Response.json(users)
  },
  POST: async ({ request }) => {
    const body = await request.json()
    const user = await db.insert(users).values(body).returning()
    return new Response(JSON.stringify(user), { status: 201 })
  },
})
typescript
// app/routes/api/users.ts
import { createAPIFileRoute } from '@tanstack/react-start/api'

export const APIRoute = createAPIFileRoute('/api/users')({
  GET: async ({ request }) => {
    const users = await db.query.users.findMany()
    return Response.json(users)
  },
  POST: async ({ request }) => {
    const body = await request.json()
    const user = await db.insert(users).values(body).returning()
    return new Response(JSON.stringify(user), { status: 201 })
  },
})

SSR Strategies

SSR策略

Streaming SSR (Default)

流式SSR(默认)

typescript
export const Route = createFileRoute('/dashboard')({
  loader: async () => ({
    criticalData: await fetchCriticalData(),
    deferredData: defer(fetchSlowData()),
  }),
  component: Dashboard,
})

function Dashboard() {
  const { criticalData, deferredData } = Route.useLoaderData()
  return (
    <div>
      <CriticalSection data={criticalData} />
      <Suspense fallback={<Loading />}>
        <Await promise={deferredData}>
          {(data) => <SlowSection data={data} />}
        </Await>
      </Suspense>
    </div>
  )
}
typescript
export const Route = createFileRoute('/dashboard')({
  loader: async () => ({
    criticalData: await fetchCriticalData(),
    deferredData: defer(fetchSlowData()),
  }),
  component: Dashboard,
})

function Dashboard() {
  const { criticalData, deferredData } = Route.useLoaderData()
  return (
    <div>
      <CriticalSection data={criticalData} />
      <Suspense fallback={<Loading />}>
        <Await promise={deferredData}>
          {(data) => <SlowSection data={data} />}
        </Await>
      </Suspense>
    </div>
  )
}

Deployment

部署

Supported Platforms (Nitro Presets)

支持的平台(Nitro预设)

typescript
// app.config.ts
export default defineConfig({
  server: {
    preset: 'node-server',        // Self-hosted Node.js
    // preset: 'vercel',          // Vercel
    // preset: 'netlify',         // Netlify
    // preset: 'cloudflare-pages', // Cloudflare Pages
    // preset: 'aws-lambda',      // AWS Lambda
    // preset: 'deno-server',     // Deno Deploy
    // preset: 'bun',             // Bun
  },
})
typescript
// app.config.ts
export default defineConfig({
  server: {
    preset: 'node-server',        // 自托管Node.js
    // preset: 'vercel',          // Vercel
    // preset: 'netlify',         // Netlify
    // preset: 'cloudflare-pages', // Cloudflare Pages
    // preset: 'aws-lambda',      // AWS Lambda
    // preset: 'deno-server',     // Deno Deploy
    // preset: 'bun',             // Bun
  },
})

Best Practices

最佳实践

  1. Use validators for all server function inputs - runtime safety and TypeScript inference
  2. Compose middleware for cross-cutting concerns (auth, logging, rate limiting)
  3. Use
    createServerFn
    GET
    for data fetching (cacheable, preloadable)
  4. Use
    createServerFn
    POST
    for mutations and side effects
  5. Use
    beforeLoad
    for route-level auth guards
  6. Use
    defer()
    for non-critical data to improve TTFB
  7. Set
    defaultPreload: 'intent'
    on the router for instant navigation
  8. Co-locate server functions with the routes that use them
  1. 为所有服务器函数输入使用验证器 - 运行时安全与TypeScript类型推断
  2. 组合中间件处理横切关注点(认证、日志、速率限制)
  3. 使用
    createServerFn
    GET
    进行数据获取(可缓存、可预加载)
  4. 使用
    createServerFn
    POST
    进行数据变更与副作用操作
  5. **使用
    beforeLoad
    **实现路由级别的认证守卫
  6. **使用
    defer()
    **处理非关键数据以提升TTFB(首字节时间)
  7. **在路由上设置
    defaultPreload: 'intent'
    **实现即时导航
  8. 将服务器函数与使用它们的路由放在一起

Common Pitfalls

常见陷阱

  • Server functions cannot close over client-side variables (they're extracted to separate bundles)
  • Data returned from server functions must be serializable
  • Forgetting
    await
    in loaders leads to streaming issues
  • Importing server-only code in client bundles causes build errors
  • Missing
    declare module '@tanstack/react-router'
    loses all type safety
  • 服务器函数无法引用客户端变量(它们会被提取到单独的打包文件中)
  • 服务器函数返回的数据必须可序列化
  • 在加载器中忘记
    await
    会导致流式渲染问题
  • 在客户端打包文件中导入仅服务器端代码会导致构建错误
  • 缺少
    declare module '@tanstack/react-router'
    会丢失所有类型安全