Доки по разработке
This project is maintained by teniryte
TanStack Router v1 — современный типобезопасный маршрутизатор для React, объединяющий дерево маршрутов, загрузку данных, интеграцию с suspense и SSR. Этот раздел поможет быстро подготовить проект к работе.
StrictMode.exports.pnpm add @tanstack/react-router @tanstack/router-devtools
pnpm add -D @tanstack/router-plugin vite-tsconfig-paths
@tanstack/react-query — если требуется координация запросов.zod/valibot — для схем параметров.@tanstack/router-devtools можно подключать условно только в разработке.@tanstack/router-core — если пишете собственный рантайм-обёртку (например, под React Native).@tanstack/router-next — для гибридных проектов на Next 13/14.Лайфхак: для песочницы или быстрого прототипа выполните pnpm dlx create-tanstack-router-app@latest, выберите шаблон (Vite/React, Expo, Next) и получите уже настроенный routeTree.
src/
routes/
__root.tsx
index.tsx
blog.$postId.tsx
main.tsx
// src/routes/__root.tsx
import { createRootRoute } from '@tanstack/react-router'
import { TanStackRouterDevtools } from '@tanstack/router-devtools'
export const Route = createRootRoute({
component: () => (
<>
<Header />
<Outlet />
{import.meta.env.DEV && <TanStackRouterDevtools />}
</>
),
errorComponent: ({ error }) => (
<ErrorLayout>
<pre>{error.message}</pre>
</ErrorLayout>
),
})
// src/routes/index.tsx
import { createRoute } from '@tanstack/react-router'
import { Route as rootRoute } from './__root'
export const Route = createRoute({
getParentRoute: () => rootRoute,
path: '/',
component: () => <div>Главная</div>,
meta: () => ({
title: 'Главная | TanStack Router v1',
}),
})
Совет: выносите общие layout-компоненты (
<Header/>,<Sidebar/>) именно вcreateRootRoute, а не вmain.tsx, чтобы они участвовали в Suspense/ErrorBoundary pipeline самого роутера.
// src/main.tsx
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(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>,
)
routeTree.gen.ts генерируется плагином (см. ниже).
Практика: настройте tsconfig.json, чтобы IDE видела автоматический модуль:
{
"compilerOptions": {
"paths": {
"~/*": ["./src/*"]
},
"types": ["@tanstack/react-router"]
}
}
// vite.config.ts
import { TanStackRouterVite } from '@tanstack/router-plugin/vite'
export default defineConfig({
plugins: [react(), TanStackRouterVite()],
})
routeTree.gen.ts. Добавьте файл в .gitignore или коммитьте, если CICD без генерации.pnpm tanstack codegen && pnpm test — CI поймает несоответствие маршрутов типам до попадания в main.Полезные опции плагина:
routesDirectory — если маршруты живут не в src/routes.generatedRouteTree — кастомное имя файла.quoteStyle: 'single' | 'double' — помогает синхронизировать форматирование с eslint/prettier.import { RouterDevtools } from '@tanstack/router-devtools'
<RouterProvider router={router}>
<RouterDevtools initialIsOpen={false} position="bottom-right" />
</RouterProvider>
pnpm run build && pnpm run preview для проверки Production-конфигурации.router.load() перед рендером на сервере.await router.load() на сервере и передавайте router.dehydrate() клиенту.playwright/cypress smoke-тесты на основные маршруты: обновление routeTree.gen.ts иногда требует перепроверить ленивые чанки.| Команда | Описание |
| — | — |
| pnpm tanstack start | Запуск плагина, генерирующего дерево маршрутов. |
| pnpm tanstack lint | Проверка схем путей/поиска. |
| pnpm tanstack codegen | Одноразовая генерация routeTree.gen.ts. |
| pnpm tanstack routes:inspect | Быстрый список маршрутов с параметрами и meta. |
| pnpm tanstack routes:types | Регенерация только типов без перезаписи дерева (удобно в CI). |
__root и хотя бы один дочерний маршрут.router в модуле типов.layout.tsx, route.tsx, loader.ts) упрощают деление обязанностей: можно держать component.tsx и loader.ts отдельно и импортировать их в route.ts.blog.$postId.tsx даёт params.postId. Используйте validateSearch/parseParams с zod, чтобы ловить ошибки до рендера.layout и управляются useRouteContext.Перегрузка ссылок: Link поддерживает preload="render" |
"intent" |
"viewport" — выбирайте стратегию заранее, чтобы не дергать сеть лишний раз. |
export const Route = createRoute({
getParentRoute: () => rootRoute,
path: '/projects/$projectId',
parseParams: (params) => ({ projectId: Number(params.projectId) }),
validateSearch: z.object({ tab: z.enum(['overview', 'settings']).default('overview') }),
})
loader работает как React 18 Suspense data-fetcher, а beforeLoad — синхронная проверка (auth/feature flags). Совмещайте: сначала сверяете токен, потом достаёте данные.context на уровне createRootRoute помогает прокинуть сервисы (apiClient, queryClient). Дочерний маршрут читает через Route.useRouteContext().action (beta) позволяет описать мутацию, возвращающую SubmissionResult. Используйте вместе с <Form> из пакета, чтобы получить автоматически типизированный fetcher.export const Route = createRoute({
getParentRoute: () => rootRoute,
path: '/profile',
loader: async ({ context }) => context.queryClient.ensureQueryData(profileQuery),
beforeLoad: ({ context, location }) => {
if (!context.auth.user) throw redirect({ to: '/login', search: { redirect: location.href } })
},
})
QueryClient в __root и сохраняйте в контексте.loader: ({ context, params }) => context.queryClient.ensureQueryData(query) для SSR-friendly загрузки.router.prefetchRoute({ to: '/blog/$postId', params: { postId } }) в onMouseEnter.defer для частичного UI: loader может возвращать defer({ hero: fetchHero(), comments: fetchComments() }).useSearch всегда типизирован через validateSearch.createFileRoute('/products').updateSearch({ category: (old) => ... }) — это заменит ручные new URLSearchParams.structuralSharing: true, чтобы роутер не пересоздавал компоненты, если search не изменился.export const Route = createRoute({
path: '/reports',
validateSearch: z.object({
from: z.coerce.date(),
to: z.coerce.date(),
tags: z.array(z.string()).default([]),
}),
loader: async ({ search }) => fetchReports(search),
})
router.invalidate() перезапустит все loader-ы текущих матчей, аналогично queryClient.invalidateQueries.router.navigate({ to: '/settings', replace: true, search: (old) => ({ ...old, tab: 'notifications' }) }) — избавляет от ручного useNavigate.useRouterState({ select: (s) => s.matches }) позволяет строить собственные breadcrumbs.useBlocker и router.history.block() помогут показать кастомный диалог перед уходом со страницы форм.На сервере:
const router = createRouter({ routeTree, context: { queryClient } })
await router.load()
const dehydrated = router.dehydrate()
renderToPipeableStream(<RouterProvider router={router} context={{ dehydrated }} />)
const router = createRouter({ routeTree, context: { queryClient }, dehydrated: window.__TSR__ })
router.hydrate()
@tanstack/router-next и createServerReference для совместного доступа к fetcher-ам.Route Matches, чтобы увидеть loader/beforeLoad и их время.router.invalidate() после перехода, чтобы наглядно увидеть, какие данные подкачиваются повторно.localStorage.__TSR_DEVTOOLS = '1' — удобно для QA./$lang с path: '$lang' и params.lang + beforeLoad, в котором проверяете доступные локали.context прокидывайте flags, а в beforeLoad делайте if (!context.flags.newDashboard) throw notFound()./_modal с component: () => <Outlet /> и useMatchRoute для открытия поверх текущей страницы — работает без дополнительного состояния.lazy: () => import('./component') вместо component, чтобы Webpack/Vite автоматически код-сплитил уровень маршрута.export const Route = createRoute({
path: '/settings',
component: SettingsPage,
errorComponent: SettingsError,
pendingComponent: () => <Spinner />,
onLoad: ({ preload }) => preload({ select: (s) => s.location }),
})
pnpm tanstack lint не нашёл ошибок в path/search.loader покрыты e2e-тестами (Cypress Playwright) с cy.waitForReact() при необходимости.router.invalidate() вызывается после критических мутаций.ErrorBoundary/pendingComponent для каждого маршрута с fetch.onLoad, onBeforeLoad для мониторингов (Sentry/Datadog).