tanstack-router-guide
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTanStack 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-pluginsh
npm install @tanstack/react-router
npm install -D @tanstack/router-pluginOptional: devtools
可选:开发者工具
npm install @tanstack/react-router-devtools
undefinednpm install @tanstack/react-router-devtools
undefinedQuick 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 and file names determine URL paths:
src/routes/| File Name | URL Path | Purpose |
|---|---|---|
| N/A | Root layout (always rendered) |
| | Index route |
| | Static route |
| | Layout route (renders Outlet) |
| | Index for /posts |
| | Dynamic segment |
| N/A | Pathless layout (wraps children, no URL) |
| | Child of pathless layout |
| | Non-nested (escapes parent layout) |
| | Splat/catch-all route |
| | Optional path parameter |
| N/A | Excluded from routing |
| | Route group (organizational only) |
The plugin auto-generates - commit this file but never edit it manually.
routeTree.gen.ts路由文件存放在目录下,文件名决定URL路径:
src/routes/| 文件名 | URL路径 | 用途 |
|---|---|---|
| N/A | 根布局(始终渲染) |
| | 索引路由 |
| | 静态路由 |
| | 布局路由(渲染Outlet) |
| | /posts的索引路由 |
| | 动态分段 |
| N/A | 无路径布局(包裹子路由,不对应URL) |
| | 无路径布局的子路由 |
| | 非嵌套路由(脱离父布局) |
| | 通配/捕获所有路由 |
| | 可选路径参数 |
| N/A | 排除在路由之外 |
| | 路由分组(仅用于组织) |
插件会自动生成文件 - 请提交该文件,但切勿手动编辑。
routeTree.gen.tsNavigation
导航
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' }) // Relativetsx
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: (SWR cache duration), (control when to re-fetch), / (loading indicator timing), (garbage collection), (search-param keying).
staleTimeshouldReloadpendingMspendingMinMsgcTimeloaderDepstsx
// 带路径参数的基础加载器
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,
})加载器关键选项:(SWR缓存时长)、(控制重新获取时机)、/(加载指示器计时)、(垃圾回收)、(搜索参数键值)。
staleTimeshouldReloadpendingMspendingMinMsgcTimeloaderDepsOptional Path Parameters
可选路径参数
Use syntax for segments that may or may not exist:
{-$paramName}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
| Hook | Purpose |
|---|---|
| Current validated search params |
| Current path params |
| Data from route loader |
| Route's context |
| Navigate from current route |
| Navigate from any component |
| Access router instance |
| Reactive router state |
| Match data for specific route |
| Block navigation (dirty forms) |
See for all 19 hooks with full signatures.
references/api-hooks.md| Hook | 用途 |
|---|---|
| 当前已验证的搜索参数 |
| 当前路径参数 |
| 路由加载器返回的数据 |
| 路由上下文 |
| 从当前路由导航 |
| 从任意组件导航 |
| 访问路由实例 |
| 响应式路由状态 |
| 特定路由的匹配数据 |
| 阻止导航(如未保存的表单) |
查看获取全部19个Hook的完整签名。
references/api-hooks.mdKey Rules
重要规则
- Plugin order: must come BEFORE
tanstackRouter()in Vite configreact() - 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.,
_) for layout-only routes_auth.tsx - Non-nested routes: Use suffix to escape parent layout (e.g.,
_)posts_.$id.edit.tsx - Ignore generated file: Add to
routeTree.gen.ts,.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 adapter
zodValidator - Outlet required: Every route with children must render ; routes without a
<Outlet />auto-rendercomponent<Outlet /> - Type safety tip: Use methods over standalone hooks for automatic type inference
Route.useX()
- 插件顺序:在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 /> - 类型安全提示:优先使用方法而非独立Hook,以实现自动类型推导
Route.useX()
Reference Files
参考文档
API
API
- — All 19 hooks with signatures, options, and examples
references/api-hooks.md - — Link, Outlet, Await, Block, HeadContent, CatchNotFound, and more
references/api-components.md - — createRouter, createFileRoute, redirect, notFound, linkOptions, search middleware
references/api-functions.md - — Router instance methods, events, and route type API
references/api-router-instance.md - — NavigateOptions, RouterState, RouteMatch, type utilities, deprecated items
references/api-types.md
- — 全部19个Hook的签名、选项及示例
references/api-hooks.md - — Link、Outlet、Await、Block、HeadContent、CatchNotFound等组件
references/api-components.md - — createRouter、createFileRoute、redirect、notFound、linkOptions、搜索中间件
references/api-functions.md - — 路由实例方法、事件及路由类型API
references/api-router-instance.md - — NavigateOptions、RouterState、RouteMatch、类型工具、已弃用项
references/api-types.md
Patterns
模式
- — Path parameters, search params (Zod/Valibot/ArkType), loaderDeps, middlewares
references/patterns-params.md - — Link options, custom links, navigation blocking, history types
references/patterns-links-blocking.md - — Data loading, mutations, TanStack Query integration, not-found handling
references/patterns-data.md - — Authentication, RBAC, router context, preloading strategies
references/patterns-auth.md
- — 路径参数、搜索参数(Zod/Valibot/ArkType)、loaderDeps、中间件
references/patterns-params.md - — Link选项、自定义链接、导航阻止、历史记录类型
references/patterns-links-blocking.md - — 数据加载、突变、TanStack Query集成、未找到处理
references/patterns-data.md - — 身份验证、RBAC、路由上下文、预加载策略
references/patterns-auth.md
Configuration
配置
- — Vite, Webpack, Rspack, esbuild, Router CLI setup and plugin options
references/config-bundlers.md - — File naming conventions, route matching, code-based routing
references/config-routing.md - — Virtual file routes, physical routes, __virtual.ts subtrees
references/config-virtual-routes.md - — All createRouter() options: core, preloading, data loading, search, scroll, URL behavior
references/config-router-options.md - — All createFileRoute/createRoute options: components, search, loader, lifecycle, head, SSR
references/config-route-options.md - — DevTools modes, production devtools, IDE configuration
references/config-devtools.md
- — Vite、Webpack、Rspack、esbuild、Router CLI的设置及插件选项
references/config-bundlers.md - — 文件命名规范、路由匹配、基于代码的路由
references/config-routing.md - — 虚拟文件路由、物理路由、__virtual.ts子树
references/config-virtual-routes.md - — createRouter()的全部选项:核心、预加载、数据加载、搜索、滚动、URL行为
references/config-router-options.md - — createFileRoute/createRoute的全部选项:组件、搜索、加载器、生命周期、头部、SSR
references/config-route-options.md - — 开发者工具模式、生产环境开发者工具、IDE配置
references/config-devtools.md
Advanced
进阶
- — SSR streaming/non-streaming, dehydration/hydration, deferred data
references/advanced-ssr.md - — Automatic/manual splitting, split groupings, lazy routes
references/advanced-code-splitting.md - — URL rewrites, route masking, custom search serialization
references/advanced-url-features.md - — Type safety, TS performance tips, render optimizations, view transitions
references/advanced-optimization.md - — Document head management, scroll restoration, i18n
references/advanced-head-scroll.md
- — SSR流式/非流式、脱水/注水、延迟数据
references/advanced-ssr.md - — 自动/手动分割、分组分割、懒加载路由
references/advanced-code-splitting.md - — URL重写、路由掩码、自定义搜索参数序列化
references/advanced-url-features.md - — 类型安全、TS性能提示、渲染优化、视图过渡
references/advanced-optimization.md - — 文档头部管理、滚动恢复、国际化
references/advanced-head-scroll.md
Operations
—
- — FAQ, common errors, debugging guide, performance issues
references/troubleshooting.md - — Deployment (8 platforms), environment variables, framework integrations
references/deployment-integrations.md - — Testing setup, route testing patterns, migration guides
references/testing-migration.md
—