tanstack-router

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

TanStack Router

TanStack Router

Version: @tanstack/react-router@1.x Requires: React 18.0+, TypeScript 5.0+, Vite (recommended)
版本:@tanstack/react-router@1.x 依赖要求:React 18.0+、TypeScript 5.0+、Vite(推荐)

Quick Setup

快速设置

bash
npm install @tanstack/react-router @tanstack/react-router-devtools
npm install -D @tanstack/router-plugin
bash
npm install @tanstack/react-router @tanstack/react-router-devtools
npm install -D @tanstack/router-plugin

Vite Plugin

Vite 插件

ts
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { tanstackRouter } from '@tanstack/router-plugin/vite'

export default defineConfig({
  plugins: [tanstackRouter(), react()],
})
ts
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { tanstackRouter } from '@tanstack/router-plugin/vite'

export default defineConfig({
  plugins: [tanstackRouter(), react()],
})

Root Route

根路由

tsx
// src/routes/__root.tsx
import { createRootRoute, Outlet } from '@tanstack/react-router'
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'

export const Route = createRootRoute({
  component: () => (
    <>
      <Outlet />
      <TanStackRouterDevtools />
    </>
  ),
})
tsx
// src/routes/__root.tsx
import { createRootRoute, Outlet } from '@tanstack/react-router'
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'

export const Route = createRootRoute({
  component: () => (
    <>
      <Outlet />
      <TanStackRouterDevtools />
    </>
  ),
})

Unified Devtools (Recommended with Multiple TanStack Libraries)

统一开发者工具(推荐搭配多个TanStack库使用)

If using Router + Query (or other TanStack libraries), use the unified
TanStackDevtools
shell instead of individual devtools components:
bash
npm install -D @tanstack/react-devtools
tsx
// src/routes/__root.tsx
import { createRootRoute, Outlet } from '@tanstack/react-router'
import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools'
import { TanStackDevtools } from '@tanstack/react-devtools'

export const Route = createRootRoute({
  component: () => (
    <>
      <Outlet />
      <TanStackDevtools
        config={{ position: 'bottom-right' }}
        plugins={[
          { name: 'TanStack Router', render: <TanStackRouterDevtoolsPanel /> },
          // Add more plugins: Query, etc.
        ]}
      />
    </>
  ),
})
Use
*Panel
variants (
TanStackRouterDevtoolsPanel
,
ReactQueryDevtoolsPanel
) when embedding inside
TanStackDevtools
.
如果同时使用 Router + Query(或其他TanStack库),请使用统一的
TanStackDevtools
外壳,而非单独的开发者工具组件:
bash
npm install -D @tanstack/react-devtools
tsx
// src/routes/__root.tsx
import { createRootRoute, Outlet } from '@tanstack/react-router'
import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools'
import { TanStackDevtools } from '@tanstack/react-devtools'

export const Route = createRootRoute({
  component: () => (
    <>
      <Outlet />
      <TanStackDevtools
        config={{ position: 'bottom-right' }}
        plugins={[
          { name: 'TanStack Router', render: <TanStackRouterDevtoolsPanel /> },
          // 添加更多插件:Query 等
        ]}
      />
    </>
  ),
})
当嵌入到
TanStackDevtools
中时,请使用
*Panel
变体(如
TanStackRouterDevtoolsPanel
ReactQueryDevtoolsPanel
)。

Router Creation & Type Registration

路由创建与类型注册

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

export const router = createRouter({ routeTree })

// Register router type for global inference
declare module '@tanstack/react-router' {
  interface Register {
    router: typeof router
  }
}
tsx
// src/router.tsx
import { createRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'

export const router = createRouter({ routeTree })

// 注册路由类型以实现全局类型推断
declare module '@tanstack/react-router' {
  interface Register {
    router: typeof router
  }
}

Entry Point

入口文件

tsx
// src/main.tsx
import { RouterProvider } from '@tanstack/react-router'
import { router } from './router'

function App() {
  return <RouterProvider router={router} />
}
tsx
// src/main.tsx
import { RouterProvider } from '@tanstack/react-router'
import { router } from './router'

function App() {
  return <RouterProvider router={router} />
}

File Structure

文件结构

src/routes/
├── __root.tsx              # Root layout (always rendered)
├── index.tsx               # "/" route
├── about.tsx               # "/about" route
├── posts.tsx               # "/posts" layout
├── posts.index.tsx         # "/posts" index
├── posts.$postId.tsx       # "/posts/:postId" dynamic route
├── _auth.tsx               # Pathless layout (auth guard)
├── _auth.dashboard.tsx     # "/dashboard" (wrapped by _auth)
└── (settings)/
    ├── settings.tsx         # Route group
    └── settings.profile.tsx
src/routes/
├── __root.tsx              # 根布局(始终渲染)
├── index.tsx               # "/" 路由
├── about.tsx               # "/about" 路由
├── posts.tsx               # "/posts" 布局
├── posts.index.tsx         # "/posts" 索引路由
├── posts.$postId.tsx       # "/posts/:postId" 动态路由
├── _auth.tsx               # 无路径布局(权限守卫)
├── _auth.dashboard.tsx     # "/dashboard"(被 _auth 包裹)
└── (settings)/
    ├── settings.tsx         # 路由分组
    └── settings.profile.tsx

Rule Categories

规则分类

PriorityCategoryRule FileImpact
CRITICALType Safety
rules/ts-type-safety.md
Prevents runtime errors, enables refactoring
CRITICALFile-Based Routing
rules/org-file-based-routing.md
Ensures maintainable route structure
HIGHRouter Config
rules/router-configuration.md
Global defaults for preload, scroll, errors
HIGHData Loading
rules/load-data-loading.md
Optimizes data fetching, prevents waterfalls
HIGHQuery Integration
rules/load-query-integration.md
TanStack Query + Router wiring
HIGHSearch Params
rules/search-params.md
Type-safe URL state management
HIGHError Handling
rules/err-error-handling.md
Graceful error and 404 handling
MEDIUMNavigation
rules/nav-navigation.md
Type-safe links and programmatic nav
MEDIUMCode Splitting
rules/split-code-splitting.md
Reduces bundle size
MEDIUMPreloading
rules/pre-preloading.md
Improves perceived performance
LOWRoute Context
rules/ctx-route-context.md
Dependency injection and auth guards
优先级分类规则文件影响
CRITICAL类型安全
rules/ts-type-safety.md
防止运行时错误,支持重构
CRITICAL基于文件的路由
rules/org-file-based-routing.md
确保路由结构可维护
HIGH路由配置
rules/router-configuration.md
预加载、滚动、错误处理的全局默认设置
HIGH数据加载
rules/load-data-loading.md
优化数据获取,避免请求瀑布
HIGHQuery 集成
rules/load-query-integration.md
TanStack Query 与 Router 的关联配置
HIGH搜索参数
rules/search-params.md
类型安全的 URL 状态管理
HIGH错误处理
rules/err-error-handling.md
优雅的错误与404处理
MEDIUM导航
rules/nav-navigation.md
类型安全的链接与编程式导航
MEDIUM代码分割
rules/split-code-splitting.md
减小包体积
MEDIUM预加载
rules/pre-preloading.md
提升感知性能
LOW路由上下文
rules/ctx-route-context.md
依赖注入与权限守卫

Critical Rules

核心规则

Always Do

必须遵循

  • Register router type — declare module
    @tanstack/react-router
    with
    Register.router
    for global type inference
  • Use
    from
    parameter
    in hooks (
    useSearch
    ,
    useParams
    ,
    useLoaderData
    ) to get exact types for the current route
  • Validate search params — use
    validateSearch
    with any Standard Schema library (Zod, Valibot, Yup, ArkType, etc.)
  • Use file-based routing — let the plugin generate the route tree, don't maintain it manually
  • Use loaders for data — fetch in
    loader
    , not in components (prevents waterfalls, enables preloading)
  • 注册路由类型 — 声明
    @tanstack/react-router
    模块的
    Register.router
    ,实现全局类型推断
  • 在钩子(
    useSearch
    useParams
    useLoaderData
    )中使用
    from
    参数,获取当前路由的精确类型
  • 验证搜索参数 — 使用
    validateSearch
    搭配任意标准 Schema 库(Zod、Valibot、Yup、ArkType 等)
  • 使用基于文件的路由 — 让插件自动生成路由树,不要手动维护
  • 使用加载器获取数据 — 在
    loader
    中请求数据,而非组件内(避免请求瀑布,支持预加载)

Never Do

禁止操作

  • Don't skip type registration — without it, all hooks return
    unknown
    unions
  • Don't fetch data in useEffect — use
    loader
    or
    beforeLoad
    instead
  • Don't use string paths without Link's type checking
    <Link to="/typo">
    catches errors at compile time
  • Don't put heavy logic in components — loaders run before render and enable preloading/parallel fetching
  • 不要跳过类型注册 — 否则所有钩子都会返回
    unknown
    联合类型
  • 不要在 useEffect 中请求数据 — 改用
    loader
    beforeLoad
  • 不要使用未经过 Link 类型检查的字符串路径
    <Link to="/typo">
    会在编译阶段捕获错误
  • 不要在组件中放置重逻辑 — 加载器在渲染前运行,支持预加载/并行请求

Key Patterns

关键模式

tsx
// Auth guard with beforeLoad + redirect
export const Route = createFileRoute('/_auth')({
  beforeLoad: ({ context }) => {
    if (!context.auth.user) {
      throw redirect({ to: '/login', search: { redirect: location.href } })
    }
  },
})

// Search params with Standard Schema (no adapter needed)
import { z } from 'zod'

const searchSchema = z.object({
  page: z.number().default(1),
  sort: z.enum(['newest', 'oldest']).default('newest'),
})

export const Route = createFileRoute('/posts')({
  validateSearch: searchSchema, // Pass schema directly
})

// Loader with ensureQueryData
export const Route = createFileRoute('/posts/$postId')({
  loader: ({ context, params }) =>
    context.queryClient.ensureQueryData(postQueryOptions(params.postId)),
  component: PostComponent,
})

function PostComponent() {
  const post = Route.useLoaderData()
  return <h1>{post.title}</h1>
}

// Code-split with .lazy.tsx
// posts.tsx — keeps loader (critical path)
export const Route = createFileRoute('/posts')({
  loader: () => fetchPosts(),
})

// posts.lazy.tsx — splits component (loaded after)
export const Route = createLazyFileRoute('/posts')({
  component: PostsComponent,
})
tsx
// 基于 beforeLoad + 重定向的权限守卫
export const Route = createFileRoute('/_auth')({
  beforeLoad: ({ context }) => {
    if (!context.auth.user) {
      throw redirect({ to: '/login', search: { redirect: location.href } })
    }
  },
})

// 搭配标准 Schema 的搜索参数(无需适配器)
import { z } from 'zod'

const searchSchema = z.object({
  page: z.number().default(1),
  sort: z.enum(['newest', 'oldest']).default('newest'),
})

export const Route = createFileRoute('/posts')({
  validateSearch: searchSchema, // 直接传入 Schema
})

// 使用 ensureQueryData 的加载器
export const Route = createFileRoute('/posts/$postId')({
  loader: ({ context, params }) =>
    context.queryClient.ensureQueryData(postQueryOptions(params.postId)),
  component: PostComponent,
})

function PostComponent() {
  const post = Route.useLoaderData()
  return <h1>{post.title}</h1>
}

// 使用 .lazy.tsx 实现代码分割
// posts.tsx — 保留加载器(关键路径)
export const Route = createFileRoute('/posts')({
  loader: () => fetchPosts(),
})

// posts.lazy.tsx — 分割组件(后续加载)
export const Route = createLazyFileRoute('/posts')({
  component: PostsComponent,
})