tanstack-router-guide

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

TanStack Router Guide (React)

TanStack Router 指南(React)

TanStack Router is a fully type-safe, file-based router for React. It provides first-class search param APIs, built-in data loading with SWR caching, automatic code splitting, and 100% inferred TypeScript types. Designed for client-first SPAs with optional SSR support.
TanStack Router是一款为React打造的完全类型安全、基于文件的路由库。它提供一流的搜索参数API、内置带SWR缓存的数据加载、自动代码分割以及100%推导的TypeScript类型。专为客户端优先的SPA设计,同时支持可选的SSR。

Install

安装

sh
npm install @tanstack/react-router
npm install -D @tanstack/router-plugin
sh
npm install @tanstack/react-router
npm install -D @tanstack/router-plugin

Optional: devtools

可选:开发者工具

npm install @tanstack/react-router-devtools
undefined
npm install @tanstack/react-router-devtools
undefined

Quick Start with Vite (Recommended)

Vite快速开始(推荐)

1. Configure 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({ target: 'react', autoCodeSplitting: true }),
    react(), // Must come AFTER tanstackRouter
  ],
})
2. Create root route:
tsx
// src/routes/__root.tsx
import { createRootRoute, Link, Outlet } from '@tanstack/react-router'
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'

export const Route = createRootRoute({
  component: () => (
    <>
      <nav>
        <Link to="/" activeProps={{ className: 'font-bold' }}>Home</Link>
        <Link to="/about" activeProps={{ className: 'font-bold' }}>About</Link>
      </nav>
      <Outlet />
      <TanStackRouterDevtools />
    </>
  ),
})
3. Create routes:
tsx
// src/routes/index.tsx
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/')({
  component: () => <div>Welcome Home!</div>,
})

// src/routes/about.tsx
export const Route = createFileRoute('/about')({
  component: () => <div>About Page</div>,
})
4. Mount the router:
tsx
// src/main.tsx
import { StrictMode } from 'react'
import ReactDOM from 'react-dom/client'
import { RouterProvider, createRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'

const router = createRouter({ routeTree })

// Register router type globally for type safety
declare module '@tanstack/react-router' {
  interface Register {
    router: typeof router
  }
}

ReactDOM.createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <RouterProvider router={router} />
  </StrictMode>,
)
1. 配置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({ target: 'react', autoCodeSplitting: true }),
    react(), // 必须在tanstackRouter之后
  ],
})
2. 创建根路由:
tsx
// src/routes/__root.tsx
import { createRootRoute, Link, Outlet } from '@tanstack/react-router'
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'

export const Route = createRootRoute({
  component: () => (
    <>
      <nav>
        <Link to="/" activeProps={{ className: 'font-bold' }}>首页</Link>
        <Link to="/about" activeProps={{ className: 'font-bold' }}>关于</Link>
      </nav>
      <Outlet />
      <TanStackRouterDevtools />
    </>
  ),
})
3. 创建路由:
tsx
// src/routes/index.tsx
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/')({
  component: () => <div>欢迎来到首页!</div>,
})

// src/routes/about.tsx
export const Route = createFileRoute('/about')({
  component: () => <div>关于页面</div>,
})
4. 挂载路由:
tsx
// src/main.tsx
import { StrictMode } from 'react'
import ReactDOM from 'react-dom/client'
import { RouterProvider, createRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'

const router = createRouter({ routeTree })

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

ReactDOM.createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <RouterProvider router={router} />
  </StrictMode>,
)

File-Based Routing (Naming Conventions)

基于文件的路由(命名规范)

Routes live in
src/routes/
and file names determine URL paths:
File NameURL PathPurpose
__root.tsx
N/ARoot layout (always rendered)
index.tsx
/
Index route
about.tsx
/about
Static route
posts.tsx
/posts
Layout route (renders Outlet)
posts.index.tsx
/posts
Index for /posts
posts.$postId.tsx
/posts/:postId
Dynamic segment
_auth.tsx
N/APathless layout (wraps children, no URL)
_auth.dashboard.tsx
/dashboard
Child of pathless layout
posts_.$postId.edit.tsx
/posts/:postId/edit
Non-nested (escapes parent layout)
files.$.tsx
/files/*
Splat/catch-all route
posts.{-$category}.tsx
/posts/:category?
Optional path parameter
-utils.tsx
N/AExcluded from routing
(group)/login.tsx
/login
Route group (organizational only)
The plugin auto-generates
routeTree.gen.ts
- commit this file but never edit it manually.
路由文件存放在
src/routes/
目录下,文件名决定URL路径:
文件名URL路径用途
__root.tsx
N/A根布局(始终渲染)
index.tsx
/
索引路由
about.tsx
/about
静态路由
posts.tsx
/posts
布局路由(渲染Outlet)
posts.index.tsx
/posts
/posts的索引路由
posts.$postId.tsx
/posts/:postId
动态分段
_auth.tsx
N/A无路径布局(包裹子路由,不对应URL)
_auth.dashboard.tsx
/dashboard
无路径布局的子路由
posts_.$postId.edit.tsx
/posts/:postId/edit
非嵌套路由(脱离父布局)
files.$.tsx
/files/*
通配/捕获所有路由
posts.{-$category}.tsx
/posts/:category?
可选路径参数
-utils.tsx
N/A排除在路由之外
(group)/login.tsx
/login
路由分组(仅用于组织)
插件会自动生成
routeTree.gen.ts
文件 - 请提交该文件,但切勿手动编辑。

Navigation

导航

tsx
import { Link, useNavigate } from '@tanstack/react-router'

// Declarative - Link component
<Link to="/posts/$postId" params={{ postId: '123' }}>View Post</Link>
<Link to="/posts" search={{ page: 2, sort: 'asc' }}>Page 2</Link>
<Link to=".." from="/posts/$postId">Back to Posts</Link>

// Active styling
<Link to="/about" activeProps={{ className: 'active' }} inactiveProps={{ className: 'dim' }}>
  About
</Link>

// Programmatic - useNavigate
const navigate = useNavigate()
navigate({ to: '/posts/$postId', params: { postId: '123' } })
navigate({ to: '/posts', search: (prev) => ({ ...prev, page: 2 }) })
navigate({ to: '..', from: '/posts/$postId' }) // Relative
tsx
import { Link, useNavigate } from '@tanstack/react-router'

// 声明式 - Link组件
<Link to="/posts/$postId" params={{ postId: '123' }}>查看文章</Link>
<Link to="/posts" search={{ page: 2, sort: 'asc' }}>第2页</Link>
<Link to=".." from="/posts/$postId">返回文章列表</Link>

// 激活状态样式
<Link to="/about" activeProps={{ className: 'active' }} inactiveProps={{ className: 'dim' }}>
  关于
</Link>

// 命令式 - useNavigate
const navigate = useNavigate()
navigate({ to: '/posts/$postId', params: { postId: '123' } })
navigate({ to: '/posts', search: (prev) => ({ ...prev, page: 2 }) })
navigate({ to: '..', from: '/posts/$postId' }) // 相对路径

Search Params (Validated & Type-Safe)

搜索参数(已验证且类型安全)

tsx
import { createFileRoute } from '@tanstack/react-router'
import { z } from 'zod'

export const Route = createFileRoute('/posts')({
  validateSearch: z.object({
    page: z.number().catch(1),
    sort: z.enum(['asc', 'desc']).optional(),
    filter: z.string().optional(),
  }),
  component: PostsPage,
})

function PostsPage() {
  const { page, sort, filter } = Route.useSearch() // Fully typed
  const navigate = Route.useNavigate()

  return (
    <button onClick={() => navigate({ search: (prev) => ({ ...prev, page: prev.page + 1 }) })}>
      Next Page
    </button>
  )
}
Search Middlewares - retain or strip params across navigations:
tsx
import { retainSearchParams, stripSearchParams } from '@tanstack/react-router'

export const Route = createFileRoute('/posts')({
  validateSearch: z.object({ page: z.number().catch(1), q: z.string().optional() }),
  search: {
    middlewares: [
      retainSearchParams(['q']),        // Keep 'q' across navigations
      stripSearchParams({ page: 1 }),    // Strip 'page' when it equals default
    ],
  },
})
tsx
import { createFileRoute } from '@tanstack/react-router'
import { z } from 'zod'

export const Route = createFileRoute('/posts')({
  validateSearch: z.object({
    page: z.number().catch(1),
    sort: z.enum(['asc', 'desc']).optional(),
    filter: z.string().optional(),
  }),
  component: PostsPage,
})

function PostsPage() {
  const { page, sort, filter } = Route.useSearch() // 完全类型化
  const navigate = Route.useNavigate()

  return (
    <button onClick={() => navigate({ search: (prev) => ({ ...prev, page: prev.page + 1 }) })}>
      下一页
    </button>
  )
}
搜索中间件 - 在导航时保留或移除参数:
tsx
import { retainSearchParams, stripSearchParams } from '@tanstack/react-router'

export const Route = createFileRoute('/posts')({
  validateSearch: z.object({ page: z.number().catch(1), q: z.string().optional() }),
  search: {
    middlewares: [
      retainSearchParams(['q']),        // 在导航时保留'q'参数
      stripSearchParams({ page: 1 }),    // 当'page'等于默认值时移除
    ],
  },
})

Data Loading

数据加载

tsx
// Basic loader with path params
export const Route = createFileRoute('/posts/$postId')({
  loader: async ({ params }) => {
    const post = await fetchPost(params.postId)
    return { post }
  },
  component: PostPage,
})

function PostPage() {
  const { post } = Route.useLoaderData() // Fully typed
  return <div>{post.title}</div>
}
tsx
// Loader with search-param dependencies
export const Route = createFileRoute('/posts')({
  validateSearch: z.object({ page: z.number().catch(1) }),
  loaderDeps: ({ search }) => ({ page: search.page }),
  loader: async ({ deps }) => fetchPosts(deps.page),
  component: PostsPage,
})
Key loader options:
staleTime
(SWR cache duration),
shouldReload
(control when to re-fetch),
pendingMs
/
pendingMinMs
(loading indicator timing),
gcTime
(garbage collection),
loaderDeps
(search-param keying).
tsx
// 带路径参数的基础加载器
export const Route = createFileRoute('/posts/$postId')({
  loader: async ({ params }) => {
    const post = await fetchPost(params.postId)
    return { post }
  },
  component: PostPage,
})

function PostPage() {
  const { post } = Route.useLoaderData() // 完全类型化
  return <div>{post.title}</div>
}
tsx
// 依赖搜索参数的加载器
export const Route = createFileRoute('/posts')({
  validateSearch: z.object({ page: z.number().catch(1) }),
  loaderDeps: ({ search }) => ({ page: search.page }),
  loader: async ({ deps }) => fetchPosts(deps.page),
  component: PostsPage,
})
加载器关键选项
staleTime
(SWR缓存时长)、
shouldReload
(控制重新获取时机)、
pendingMs
/
pendingMinMs
(加载指示器计时)、
gcTime
(垃圾回收)、
loaderDeps
(搜索参数键值)。

Optional Path Parameters

可选路径参数

Use
{-$paramName}
syntax for segments that may or may not exist:
tsx
// src/routes/posts.{-$category}.tsx -> /posts or /posts/tech
export const Route = createFileRoute('/posts/{-$category}')({
  component: () => {
    const { category } = Route.useParams() // category: string | undefined
    return <div>{category ? `Posts in ${category}` : 'All Posts'}</div>
  },
})

// Navigation: pass undefined to omit the segment
<Link to="/posts/{-$category}" params={{ category: undefined }}>All Posts</Link>
<Link to="/posts/{-$category}" params={{ category: 'tech' }}>Tech Posts</Link>
使用
{-$paramName}
语法定义可选的路由分段:
tsx
// src/routes/posts.{-$category}.tsx -> /posts 或 /posts/tech
export const Route = createFileRoute('/posts/{-$category}')({
  component: () => {
    const { category } = Route.useParams() // category: string | undefined
    return <div>{category ? `${category}分类文章` : '全部文章'}</div>
  },
})

// 导航:传入undefined以省略该分段
<Link to="/posts/{-$category}" params={{ category: undefined }}>全部文章</Link>
<Link to="/posts/{-$category}" params={{ category: 'tech' }}>科技分类文章</Link>

Router Context (Dependency Injection)

路由上下文(依赖注入)

tsx
import { createRootRouteWithContext } from '@tanstack/react-router'
import type { QueryClient } from '@tanstack/react-query'

interface RouterContext {
  queryClient: QueryClient
  auth: AuthState
}

// Root route
const rootRoute = createRootRouteWithContext<RouterContext>()({
  component: RootComponent,
})

// Use in any route
export const Route = createFileRoute('/posts')({
  beforeLoad: ({ context }) => {
    // context.queryClient and context.auth available here
  },
  loader: ({ context }) => context.queryClient.ensureQueryData(postsQueryOptions()),
})

// Provide context when creating router
const router = createRouter({
  routeTree,
  context: { queryClient, auth: { user: null } },
})
tsx
import { createRootRouteWithContext } from '@tanstack/react-router'
import type { QueryClient } from '@tanstack/react-query'

interface RouterContext {
  queryClient: QueryClient
  auth: AuthState
}

// 根路由
const rootRoute = createRootRouteWithContext<RouterContext>()({
  component: RootComponent,
})

// 在任意路由中使用
export const Route = createFileRoute('/posts')({
  beforeLoad: ({ context }) => {
    // 此处可访问context.queryClient和context.auth
  },
  loader: ({ context }) => context.queryClient.ensureQueryData(postsQueryOptions()),
})

// 创建路由时提供上下文
const router = createRouter({
  routeTree,
  context: { queryClient, auth: { user: null } },
})

Authentication (Protected Routes)

身份验证(受保护路由)

tsx
// src/routes/_auth.tsx - Pathless layout for protected routes
export const Route = createFileRoute('/_auth')({
  beforeLoad: async ({ context, location }) => {
    if (!context.auth.user) {
      throw redirect({
        to: '/login',
        search: { redirect: location.href },
      })
    }
  },
  component: () => <Outlet />,
})

// src/routes/_auth.dashboard.tsx - Protected route
export const Route = createFileRoute('/_auth/dashboard')({
  component: () => <div>Protected Dashboard</div>,
})
tsx
// src/routes/_auth.tsx - 受保护路由的无路径布局
export const Route = createFileRoute('/_auth')({
  beforeLoad: async ({ context, location }) => {
    if (!context.auth.user) {
      throw redirect({
        to: '/login',
        search: { redirect: location.href },
      })
    }
  },
  component: () => <Outlet />,
})

// src/routes/_auth.dashboard.tsx - 受保护路由
export const Route = createFileRoute('/_auth/dashboard')({
  component: () => <div>受保护的控制台</div>,
})

Error Handling

错误处理

tsx
import { notFound } from '@tanstack/react-router'

export const Route = createFileRoute('/posts/$postId')({
  loader: async ({ params }) => {
    const post = await fetchPost(params.postId)
    if (!post) throw notFound()
    return { post }
  },
  notFoundComponent: () => <div>Post not found!</div>,
  errorComponent: ({ error, reset }) => (
    <div>
      <p>Error: {error.message}</p>
      <button onClick={reset}>Retry</button>
    </div>
  ),
})

// Global defaults on router
const router = createRouter({
  routeTree,
  defaultNotFoundComponent: () => <div>Page not found</div>,
  defaultErrorComponent: ({ error }) => <div>Error: {error.message}</div>,
})
tsx
import { notFound } from '@tanstack/react-router'

export const Route = createFileRoute('/posts/$postId')({
  loader: async ({ params }) => {
    const post = await fetchPost(params.postId)
    if (!post) throw notFound()
    return { post }
  },
  notFoundComponent: () => <div>文章不存在!</div>,
  errorComponent: ({ error, reset }) => (
    <div>
      <p>错误:{error.message}</p>
      <button onClick={reset}>重试</button>
    </div>
  ),
})

// 路由全局默认配置
const router = createRouter({
  routeTree,
  defaultNotFoundComponent: () => <div>页面不存在</div>,
  defaultErrorComponent: ({ error }) => <div>错误:{error.message}</div>,
})

Essential Hooks

核心Hook

HookPurpose
Route.useSearch()
Current validated search params
Route.useParams()
Current path params
Route.useLoaderData()
Data from route loader
Route.useRouteContext()
Route's context
Route.useNavigate()
Navigate from current route
useNavigate()
Navigate from any component
useRouter()
Access router instance
useRouterState()
Reactive router state
useMatch({ from: '/route' })
Match data for specific route
useBlocker()
Block navigation (dirty forms)
See
references/api-hooks.md
for all 19 hooks with full signatures.
Hook用途
Route.useSearch()
当前已验证的搜索参数
Route.useParams()
当前路径参数
Route.useLoaderData()
路由加载器返回的数据
Route.useRouteContext()
路由上下文
Route.useNavigate()
从当前路由导航
useNavigate()
从任意组件导航
useRouter()
访问路由实例
useRouterState()
响应式路由状态
useMatch({ from: '/route' })
特定路由的匹配数据
useBlocker()
阻止导航(如未保存的表单)
查看
references/api-hooks.md
获取全部19个Hook的完整签名。

Key Rules

重要规则

  • Plugin order:
    tanstackRouter()
    must come BEFORE
    react()
    in Vite config
  • Commit routeTree.gen.ts: It's runtime code, not a build artifact
  • Module declaration: Always register router type for global type inference
  • Export as Route: File-based routes must export
    const Route = createFileRoute(...)
  • Pathless layouts: Prefix with
    _
    (e.g.,
    _auth.tsx
    ) for layout-only routes
  • Non-nested routes: Use
    _
    suffix to escape parent layout (e.g.,
    posts_.$id.edit.tsx
    )
  • Ignore generated file: Add
    routeTree.gen.ts
    to
    .prettierignore
    ,
    .eslintignore
  • Route matching order: Index > Static > Dynamic > Splat (automatic)
  • Don't export route properties: Exported components/loaders break code splitting
  • Validation adapters: Valibot/ArkType work directly; Zod needs
    zodValidator
    adapter
  • Outlet required: Every route with children must render
    <Outlet />
    ; routes without a
    component
    auto-render
    <Outlet />
  • Type safety tip: Use
    Route.useX()
    methods over standalone hooks for automatic type inference
  • 插件顺序:在Vite配置中,
    tanstackRouter()
    必须在
    react()
    之前
  • 提交routeTree.gen.ts:它是运行时代码,而非构建产物
  • 模块声明:始终注册路由类型以实现全局类型推导
  • 导出为Route:基于文件的路由必须导出
    const Route = createFileRoute(...)
  • 无路径布局:前缀使用
    _
    (如
    _auth.tsx
    )表示仅布局路由
  • 非嵌套路由:使用
    _
    后缀脱离父布局(如
    posts_.$id.edit.tsx
  • 忽略生成文件:将
    routeTree.gen.ts
    添加到
    .prettierignore
    .eslintignore
  • 路由匹配顺序:索引路由 > 静态路由 > 动态路由 > 通配路由(自动排序)
  • 不要导出路由属性:导出的组件/加载器会破坏代码分割
  • 验证适配器:Valibot/ArkType可直接使用;Zod需要
    zodValidator
    适配器
  • 必须使用Outlet:所有包含子路由的路由必须渲染
    <Outlet />
    ;无
    component
    的路由会自动渲染
    <Outlet />
  • 类型安全提示:优先使用
    Route.useX()
    方法而非独立Hook,以实现自动类型推导

Reference Files

参考文档

API

API

  • references/api-hooks.md
    — All 19 hooks with signatures, options, and examples
  • references/api-components.md
    — Link, Outlet, Await, Block, HeadContent, CatchNotFound, and more
  • references/api-functions.md
    — createRouter, createFileRoute, redirect, notFound, linkOptions, search middleware
  • references/api-router-instance.md
    — Router instance methods, events, and route type API
  • references/api-types.md
    — NavigateOptions, RouterState, RouteMatch, type utilities, deprecated items
  • references/api-hooks.md
    — 全部19个Hook的签名、选项及示例
  • references/api-components.md
    — Link、Outlet、Await、Block、HeadContent、CatchNotFound等组件
  • references/api-functions.md
    — createRouter、createFileRoute、redirect、notFound、linkOptions、搜索中间件
  • references/api-router-instance.md
    — 路由实例方法、事件及路由类型API
  • references/api-types.md
    — NavigateOptions、RouterState、RouteMatch、类型工具、已弃用项

Patterns

模式

  • references/patterns-params.md
    — Path parameters, search params (Zod/Valibot/ArkType), loaderDeps, middlewares
  • references/patterns-links-blocking.md
    — Link options, custom links, navigation blocking, history types
  • references/patterns-data.md
    — Data loading, mutations, TanStack Query integration, not-found handling
  • references/patterns-auth.md
    — Authentication, RBAC, router context, preloading strategies
  • references/patterns-params.md
    — 路径参数、搜索参数(Zod/Valibot/ArkType)、loaderDeps、中间件
  • references/patterns-links-blocking.md
    — Link选项、自定义链接、导航阻止、历史记录类型
  • references/patterns-data.md
    — 数据加载、突变、TanStack Query集成、未找到处理
  • references/patterns-auth.md
    — 身份验证、RBAC、路由上下文、预加载策略

Configuration

配置

  • references/config-bundlers.md
    — Vite, Webpack, Rspack, esbuild, Router CLI setup and plugin options
  • references/config-routing.md
    — File naming conventions, route matching, code-based routing
  • references/config-virtual-routes.md
    — Virtual file routes, physical routes, __virtual.ts subtrees
  • references/config-router-options.md
    — All createRouter() options: core, preloading, data loading, search, scroll, URL behavior
  • references/config-route-options.md
    — All createFileRoute/createRoute options: components, search, loader, lifecycle, head, SSR
  • references/config-devtools.md
    — DevTools modes, production devtools, IDE configuration
  • references/config-bundlers.md
    — Vite、Webpack、Rspack、esbuild、Router CLI的设置及插件选项
  • references/config-routing.md
    — 文件命名规范、路由匹配、基于代码的路由
  • references/config-virtual-routes.md
    — 虚拟文件路由、物理路由、__virtual.ts子树
  • references/config-router-options.md
    — createRouter()的全部选项:核心、预加载、数据加载、搜索、滚动、URL行为
  • references/config-route-options.md
    — createFileRoute/createRoute的全部选项:组件、搜索、加载器、生命周期、头部、SSR
  • references/config-devtools.md
    — 开发者工具模式、生产环境开发者工具、IDE配置

Advanced

进阶

  • references/advanced-ssr.md
    — SSR streaming/non-streaming, dehydration/hydration, deferred data
  • references/advanced-code-splitting.md
    — Automatic/manual splitting, split groupings, lazy routes
  • references/advanced-url-features.md
    — URL rewrites, route masking, custom search serialization
  • references/advanced-optimization.md
    — Type safety, TS performance tips, render optimizations, view transitions
  • references/advanced-head-scroll.md
    — Document head management, scroll restoration, i18n
  • references/advanced-ssr.md
    — SSR流式/非流式、脱水/注水、延迟数据
  • references/advanced-code-splitting.md
    — 自动/手动分割、分组分割、懒加载路由
  • references/advanced-url-features.md
    — URL重写、路由掩码、自定义搜索参数序列化
  • references/advanced-optimization.md
    — 类型安全、TS性能提示、渲染优化、视图过渡
  • references/advanced-head-scroll.md
    — 文档头部管理、滚动恢复、国际化

Operations

  • references/troubleshooting.md
    — FAQ, common errors, debugging guide, performance issues
  • references/deployment-integrations.md
    — Deployment (8 platforms), environment variables, framework integrations
  • references/testing-migration.md
    — Testing setup, route testing patterns, migration guides