Skip to content

参考

基于 React Router v7.x(react-router + @react-router/dev)—— API 速查 / 文件约定 / 配置选项 / 命名约定。本笔记面向 Framework 模式

包结构

用途必需
react-router核心运行时(Hooks / Components / Router 工厂 / 工具函数)
react-router/domBrowser hydration 入口(HydratedRouterclient 入口
@react-router/devVite plugin + CLI(reactRouter() / react-router build / typegen(Framework 模式)
@react-router/dev/routes路由配置 helper(route / index / layout / prefix
@react-router/dev/config配置文件类型(Config
@react-router/fs-routes文件系统路由扫描器(flatRoutes()可选
@react-router/nodeNode.js runtime adapterNode 部署
@react-router/serve简易 Express 服务器(dev / 小项目)默认部署
@react-router/expressExpress middleware adapter自定义 Express
@react-router/cloudflareCloudflare Workers / Pages adapterCF 部署
@react-router/architectAWS Lambda + API Gateway adapterAWS
isbotUA 检测(区分爬虫与浏览器)SSR 时推荐

CLI(@react-router/dev

命令用途
react-router dev开发模式(Vite SSR HMR,端口 5173)
react-router build生产构建(client + server bundle)
react-router-serve <build>启动 Express SSR 服务器(端口 3000)
react-router typegen生成 .react-router/types/+types/*.d.ts
react-router typegen --watchWatch 模式生成类型
react-router routes打印当前路由树

react-router.config.ts 配置

ts
import type { Config } from '@react-router/dev/config'

export default {
  // ── 渲染策略 ──
  /** 是否启用 SSR(默认 true) */
  ssr: true,
  /** 静态预渲染:true / string[] / async function */
  prerender: ['/', '/about'],
  // prerender: { paths: [...], concurrency: 4 },
  // prerender: async ({ getStaticPaths }) => [...]

  // ── 目录配置 ──
  /** 应用代码目录(默认 'app') */
  appDirectory: 'app',
  /** 路由清单路径(默认 'app/routes.ts') */
  routes: 'app/routes.ts',
  /** 构建输出根目录(默认 'build') */
  buildDirectory: 'build',
  /** server bundle 文件名(默认 'index.js') */
  serverBuildFile: 'index.js',

  // ── 部署 ──
  /** Module format(默认 'esm') */
  serverModuleFormat: 'esm',

  // ── future flags(启用 unstable 特性) ──
  future: {
    unstable_middleware: false,
    unstable_optimizeDeps: false,
    unstable_splitRouteModules: false,
    unstable_subResourceIntegrity: false,
    unstable_viteEnvironmentApi: false,
  },
} satisfies Config

vite.config.ts

ts
import { reactRouter } from '@react-router/dev/vite'
import tsconfigPaths from 'vite-tsconfig-paths'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    reactRouter(),
    tsconfigPaths(), // 可选:让 ~/xxx 等路径别名生效
  ],
})

app/routes.ts Helper API

ts
import {
  type RouteConfig,
  index,
  layout,
  prefix,
  route,
} from '@react-router/dev/routes'
Helper签名用途
indexindex(file: string, opts?: { id? }): RouteConfigEntry父路由的默认子路由(路径同父级)
routeroute(path: string, file: string, opts?, children?): RouteConfigEntry标准路由
layoutlayout(file: string, opts?, children?): RouteConfigEntry纯布局(不增加 URL 段)
prefixprefix(path: string, children: RouteConfigEntry[]): RouteConfigEntry[]给一组路由批量加 URL 前缀
ts
export default [
  index('./routes/home.tsx'),
  route('about', './routes/about.tsx'),
  route('dashboard', './routes/dashboard.tsx', [
    index('./routes/dashboard-home.tsx'),
    route('settings', './routes/dashboard-settings.tsx'),
  ]),
  layout('./routes/auth-layout.tsx', [
    route('login', './routes/login.tsx'),
    route('register', './routes/register.tsx'),
  ]),
  ...prefix('admin', [
    route('users', './routes/admin-users.tsx'),
  ]),
] satisfies RouteConfig

URL 模式语法

  • /users/:id — 动态段
  • /users/:id? — 可选段
  • /files/* — splat(剩余路径)
  • :lang?/categories — 可选前缀

@react-router/fs-routes 文件路由

ts
import { type RouteConfig } from '@react-router/dev/routes'
import { flatRoutes } from '@react-router/fs-routes'

export default flatRoutes({
  rootDirectory: 'routes', // 路由目录(相对 app/)
  ignoredRouteFiles: ['**/*.css'], // 排除的文件
}) satisfies RouteConfig

命名约定(flat routes):

文件名URL
_index.tsx/
about.tsx/about
concerts.tsx/concerts(父布局)
concerts._index.tsx/concerts(index 子路由)
concerts.trending.tsx/concerts/trending
concerts.$city.tsx/concerts/:city
_auth.tsx(无 URL 段,pathless layout)
_auth.login.tsx/login(嵌套在 _auth 下)
($lang)._index.tsx//:lang
files.$.tsx/files/*
sitemap[.]xml.tsx/sitemap.xml(转义 .
app._index/route.tsx/ + 同目录辅助文件

Route Module 导出

每个路由文件(app/routes/*.tsx)可导出以下成员,全部可选

导出类型何时运行用途
defaultReact.FC<Route.ComponentProps>渲染时组件(必有才能渲染 UI)
loader(args: Route.LoaderArgs) => Promise<any>服务器服务端数据加载
clientLoader(args: Route.ClientLoaderArgs) => Promise<any>客户端客户端数据加载
clientLoader.hydratetrueclientLoader 参与初始 hydration
action(args: Route.ActionArgs) => Promise<any>服务器服务端 mutation
clientAction(args: Route.ClientActionArgs) => Promise<any>客户端客户端 mutation
meta(args: Route.MetaArgs) => MetaDescriptor[]渲染时<meta> / <title> 标签
links() => LinkDescriptor[]渲染时<link> 标签
headers(args) => HeadersInit服务器HTTP 响应头
ErrorBoundaryReact.FC<Route.ErrorBoundaryProps>出错时错误边界
HydrateFallbackReact.FC<Route.HydrateFallbackProps>hydration 期间SSR fallback UI
shouldRevalidate(args: ShouldRevalidateFunctionArgs) => booleannavigation 后控制是否 revalidate
handleunknown渲染时自定义数据(useMatches
middlewareMiddleware[](unstable)服务器服务端中间件
clientMiddlewareClientMiddleware[](unstable)客户端客户端中间件

Route.LoaderArgs

ts
interface LoaderArgs {
  request: Request // Web Standard Request
  params: Record<string, string> // URL 参数(类型从 URL pattern 推导)
  context: AppLoadContext // 自定义 context(自定义服务器传入)
}

Route.ActionArgs

ts
interface ActionArgs extends LoaderArgs {
  // request.method 通常是 POST/PUT/PATCH/DELETE
  // request.formData() 拿到表单数据
  // request.json() 拿 JSON body
}

Route.ClientLoaderArgs

ts
interface ClientLoaderArgs {
  request: Request
  params: Record<string, string>
  serverLoader: () => Promise<unknown> // 调用服务端 loader
}

Route.ClientActionArgs

ts
interface ClientActionArgs {
  request: Request
  params: Record<string, string>
  serverAction: () => Promise<unknown> // 调用服务端 action
}

Route.ComponentProps

ts
interface ComponentProps {
  loaderData: < loader 返回值推导>
  actionData: < action 返回值推导> | undefined
  params: Record<string, string>
  matches: RouteMatch[]
}

Route.MetaArgs

ts
interface MetaArgs {
  data: <loader 返回值> | undefined
  params: Record<string, string>
  location: Location
  matches: MetaMatch[]
}

Route.ErrorBoundaryProps

ts
interface ErrorBoundaryProps {
  error: unknown // 可能是 ErrorResponse / Error / 其他
  params: Record<string, string>
  loaderData: <可能 undefined>
  actionData: <可能 undefined>
}

Components(react-router 导出)

路由相关

组件用途
<Outlet />渲染当前路由的子路由
<Outlet context={...} />通过 useOutletContext() 向子路由传 context
<Link to="/path">客户端导航
<Link to="/path" prefetch="intent">鼠标 hover/focus 时预取
<NavLink to="/path">带 active / pending 状态的 Link
<Navigate to="/path" />渲染时立即重定向(声明式)
<Routes> + <Route>Declarative 模式路由树(不推荐 Framework 用)
tsx
import { Link, NavLink, Outlet } from 'react-router'

<Link to="/about" prefetch="intent">关于</Link>

<NavLink
  to="/dashboard"
  end
  className={({ isActive, isPending }) =>
    isActive ? 'active' : isPending ? 'pending' : ''}
>
  Dashboard
</NavLink>

<Outlet />

表单相关

组件用途
<Form method="post" action="/x">增强 <form>(progressive enhancement)
<Form method="get">提交为 URL search params
<Await resolve={promise}>处理 streaming Promise(配 <Suspense>
tsx
import { Await, Form } from 'react-router'

<Form method="post" action="/api/save">
  <input name="title" />
  <button>提交</button>
</Form>

<React.Suspense fallback={<Loading />}>
  <Await resolve={slowDataPromise}>
    {value => <DataView data={value} />}
  </Await>
</React.Suspense>

文档相关(用在 root.tsx

组件用途
<Meta />聚合所有路由 meta 导出
<Links />聚合所有路由 links 导出
<Scripts />注入 hydration 脚本(必需
<ScrollRestoration />客户端导航恢复滚动位置
<PrefetchPageLinks page="/x" />编程式预取

Router(程序化)

组件模式
<HydratedRouter />Framework client 入口(从 react-router/dom
<ServerRouter context={...} url={...} />Framework server 入口
<RouterProvider router={...} />Data 模式(createBrowserRouter 创建)
<BrowserRouter />Declarative 模式(v6 兼容)
<HashRouter />用 hash 而非 history API
<MemoryRouter />测试 / 嵌入式(内存中)
<StaticRouter />自定义 SSR 用

Hooks(react-router 导出)

数据访问

Hook签名用途
useLoaderData<T>()(): T取当前路由 loader 数据
useActionData<T>()(): T | undefined取当前路由 action 返回值
useRouteLoaderData<T>(id)(routeId): T | undefined取指定路由的 loader 数据
useMatches()(): RouteMatch[]所有匹配路由(含 handle
useParams<T>()(): T当前路由所有 URL 参数
useLocation()(): Location当前 URL location 对象
useSearchParams()(): [URLSearchParams, setter]读 / 写 URL search params
useMatch(pattern)(): PathMatch | null检查当前 URL 是否匹配 pattern
tsx
import {
  useActionData,
  useLoaderData,
  useMatches,
  useParams,
  useSearchParams,
} from 'react-router'

const data = useLoaderData<typeof loader>()
const action = useActionData<typeof action>()
const { id } = useParams<{ id: string }>()
const [searchParams, setSearchParams] = useSearchParams()
const matches = useMatches() // [{ id, pathname, params, data, handle, ... }, ...]

导航

Hook签名用途
useNavigate()(): NavigateFunction编程式导航
useNavigation()(): Navigation全局 navigation 状态
useNavigationType()(): 'POP' | 'PUSH' | 'REPLACE'上次导航类型
useHref(to)(to: To): string解析 To 为完整 URL(含 basename)
useResolvedPath(to)(to: To): Path解析为绝对路径
useFormAction(action?)(action?: string, opts?): string计算当前路由 action URL
tsx
import { useNavigate, useNavigation } from 'react-router'

const navigate = useNavigate()
navigate('/dashboard') // push
navigate('/dashboard', { replace: true })
navigate(-1) // 后退
navigate('/profile', { preventScrollReset: true })

const navigation = useNavigation()
// navigation.state: 'idle' | 'loading' | 'submitting'
// navigation.location: 目标 URL
// navigation.formData / formAction / formMethod

Fetcher(无导航 mutation / 数据加载)

Hook签名用途
useFetcher<T>()(): Fetcher<T>独立 fetcher 实例
useFetcher<T>({ key })({ key }): Fetcher<T>命名 fetcher(跨组件共享状态)
useFetchers()(): Fetcher[]所有活跃 fetcher(全局加载条用)
tsx
import { useFetcher } from 'react-router'

const fetcher = useFetcher<typeof action>()
// fetcher.Form / fetcher.submit / fetcher.load
// fetcher.state: 'idle' | 'submitting' | 'loading'
// fetcher.data: action / loader 返回值
// fetcher.formData / formAction / formMethod

Revalidation

Hook签名用途
useRevalidator()(): { state, revalidate }主动触发所有 loader revalidate
tsx
const revalidator = useRevalidator()
revalidator.revalidate() // 触发所有 loader 重跑
// revalidator.state: 'idle' | 'loading'

Form / Submit

Hook签名用途
useSubmit()(): SubmitFunction编程式提交(触发 action + 导航)
useLinkClickHandler(to, opts?)(): clickHandler自定义 Link click
tsx
const submit = useSubmit()
submit(formData, { method: 'post', action: '/save' })
submit(null, { method: 'post' }) // 无数据提交

错误 / 边界

Hook签名用途
useRouteError()(): unknown在 ErrorBoundary 内取 error
useAsyncError()(): unknown<Await> 的 errorElement 内取 error
useAsyncValue()(): unknown<Await> 的 children 内取 value(非 render-prop 用法)
tsx
import { isRouteErrorResponse, useRouteError } from 'react-router'

function ErrorBoundary() {
  const error = useRouteError()
  if (isRouteErrorResponse(error)) {
    return <div>{error.status} {error.statusText}</div>
  }
  return <div>Unknown</div>
}

阻塞 / 监听

Hook签名用途
useBlocker(shouldBlock)((args) => boolean): Blocker拦截 navigation(确认离开未保存修改)
useBeforeUnload(handler, opts?)(handler): void浏览器关闭前回调
usePrompt(opts)({ when, message })简化版 useBlocker(v6 风格)
tsx
const blocker = useBlocker(
  ({ currentLocation, nextLocation }) =>
    isDirty && currentLocation.pathname !== nextLocation.pathname,
)
if (blocker.state === 'blocked') {
  // 显示确认对话框
  // blocker.proceed() / blocker.reset()
}

Outlet Context

Hook签名用途
useOutletContext<T>()(): T接收父路由 <Outlet context={...} /> 传入的值
useOutlet()(): React.ReactElement | null拿到要渲染的子路由(手动控制)
tsx
// 父路由
<Outlet context={{ user, theme }} />

// 子路由
const { user, theme } = useOutletContext<{ user: User, theme: Theme }>()

View Transitions

Hook签名用途
useViewTransitionState(to)(to: To): booleanView Transition 进行中
tsx
<Link to="/about" viewTransition>关于</Link>
// 然后用 useViewTransitionState 检测

Router Context(高级)

Hook签名用途
useInRouterContext()(): boolean检查是否在 Router context 内
useRoutes(routes)(routes: RouteObject[], opts?): React.ReactElement渲染配置式路由
useRouterState()(): RouterStateData Router 完整状态
useResolvedPath(to)(to: To): Path解析 to 为 path

工具函数(react-router 导出)

响应

函数签名用途
redirect(url, init?)(url: string, init?): Response302 重定向 Response
redirectDocument(url, init?)(url: string, init?): Response文档级重定向(不走 client navigation)
replace(url, init?)(url: string, init?): Responsereplace 重定向
data(value, init?)(value, init?): Response包装数据 + 自定义 status / headers
ts
return redirect('/dashboard')
return data({ ok: false, error: '...' }, { status: 400 })
return data(payload, { headers: { 'Cache-Control': 'no-store' } })

Cookies / Sessions

函数签名用途
createCookie(name, opts?)(name, opts): Cookie创建签名 cookie
createCookieSessionStorage<D, F>(opts)(opts): SessionStorageCookie-based session
createSessionStorage<D, F>(strategy)(strategy): SessionStorage自定义存储策略(Redis / DB)
createMemorySessionStorage(opts?)(opts): SessionStorage内存 session(测试)
ts
const userPrefs = createCookie('user-prefs', {
  httpOnly: true,
  maxAge: 60 * 60 * 24 * 7,
  secrets: ['secret1'],
})

const { getSession, commitSession, destroySession }
  = createCookieSessionStorage({
    cookie: { name: '__session', secrets: ['s1'] },
  })

路由匹配

函数签名用途
matchRoutes(routes, location)(routes, location, basename?): RouteMatch[] | null给定路由树和 URL,返回匹配结果
matchPath(pattern, pathname)(pattern, pathname): PathMatch | null单个路径匹配
generatePath(pattern, params?)(pattern, params?): string用 params 替换 pattern 中的占位符
parsePath(path)(path): Partial<Path>解析 URL 为 { pathname, search, hash }
resolvePath(to, fromPathname?)(to, from?): Path相对路径解析
createPath(path)(path): string反向生成 URL

类型守卫

函数签名用途
isRouteErrorResponse(error)(error): error is ErrorResponse区分 throw new Response vs throw Error

Static handler(自定义 SSR)

函数签名用途
createStaticHandler(routes, opts?)(routes, opts): StaticHandler创建静态 handler(自定义 SSR 用)
createStaticRouter(routes, context, opts?)(routes, context, opts): Router<StaticRouterProvider> 使用

渲染策略对比

模式react-router.config.ts部署适用
SSR{ ssr: true }需要 Node / Workers动态内容 / SEO 友好
SPA{ ssr: false }静态 host(CF Pages / GitHub Pages)后台管理 / 内部工具
SSG(含 SSR){ ssr: true, prerender: [...] }任意内容站 + 部分动态
完全静态{ ssr: false, prerender: true }静态 host纯静态站
SPA Fallback{ ssr: false, prerender: ['/'] }静态 hostSEO 关键页 + SPA 其余

Adapter 部署

Adapter适用平台
Node Express@react-router/expressNode.js 自定义 Express
Node Serve@react-router/serve内置极简 Express(默认)
Node@react-router/node通用 Node runtime
Cloudflare@react-router/cloudflareCloudflare Workers / Pages
AWS Architect@react-router/architectAWS Lambda + API Gateway
Bun(内置 Node compat)Bun runtime
Deno(内置 Web standard)Deno deploy

官方模板npx create-react-router --template):

  • remix-run/react-router-templates/default — Node Docker + Tailwind
  • remix-run/react-router-templates/node-custom-server — 自定义 Express
  • remix-run/react-router-templates/node-postgres — + Postgres + Drizzle
  • remix-run/react-router-templates/cloudflare — CF Workers
  • remix-run/react-router-templates/vercel — Vercel
  • remix-run/react-router-templates/aws — AWS Lambda
  • remix-run/react-router-templates/netlify — Netlify

关键文件约定

app/
├── root.tsx                    # ✨ 必需,根路由 / HTML 文档骨架
├── routes.ts                   # ✨ 路由清单
├── routes/                     # ⚙️ 默认路由目录(约定,可改)
│   ├── home.tsx
│   ├── about.tsx
│   └── ...
├── entry.client.tsx            # 客户端入口(可选,默认隐式)
├── entry.server.tsx            # 服务端入口(可选)
├── load-context.ts             # ⚙️ AppLoadContext 类型扩展
├── *.server.ts                 # 仅服务端模块(client bundle 中报错)
├── *.client.ts                 # 仅客户端模块(server bundle 中为 undefined)
└── app.css                     # 全局样式

public/                         # 静态资源
.react-router/types/+types/     # ⚙️ 类型生成(不提交)
build/client/                   # 客户端 bundle
build/server/                   # 服务端 bundle
react-router.config.ts          # ✨ 全局配置
vite.config.ts                  # Vite 配置
tsconfig.json

命名约定

类型风格
路由模块文件名kebab-case.tsxflat.routes.tsxuser-profile.tsx / users.$id.tsx
组件名PascalCaseUserProfile
Loader / Action 函数loader / actionexport async function loader() {}
类型 import 别名Routeimport type { Route } from './+types/...'
Server-only 模块*.server.tsdb.server.ts / email.server.ts
Client-only 模块*.client.tsmonaco.client.ts
Session secret envSESSION_SECRET.env
Cookie envCOOKIE_SECRET.env

三种模式 API 速查

FeatureDeclarativeDataFramework
<BrowserRouter> / <Routes> / <Route>
createBrowserRouter / <RouterProvider>
Vite plugin (@react-router/dev)
routes.ts + file routes
loader / action
useFetcher
useNavigation
Pending UI / Optimistic UI
类型自动生成 (Route.LoaderArgs)
SSR / SSG / SPA 模式手动✓ 内置
ErrorBoundary 路由级
Sessions / Cookies
react-router-serve
Adapter

与同类对比

维度React Router v7 FrameworkNext.js 15 App RouterRemix v2SolidStartSvelteKitQwik City
渲染SSR/SPA/SSGSSR + RSCSSRSSR/SSG/CSRSSR/SSG/CSRResumable
路由routes.ts / filefilefilefilefilefile
数据加载loaderRSC / fetch()loaderquery()+page.ts loadrouteLoader$
Mutationaction + FormServer Actionsaction + Formaction()+page.server.ts actionrouteAction$
表单<Form> (PE)Server Actions / 手动<Form> (PE)<form action={x}><form> (PE)<Form> (PE)
服务器组件unstable RSC✓ 默认
类型自动生成部分部分✓ (v2+)
包体积(最小)~50KB~85KB+~50KB~20KB~20KB~1KB (resumable)
Adapter 数量7+Vercel-first10+17+ (Nitro)10+10+
学习曲线中(三模式)陡(RSC)中(Solid)陡(resumable)
生态规模最大次大(已合并)

资源链接