Dev Highlights

Доки по разработке

This project is maintained by teniryte

Обзор

TanStack Router v1 — современный типобезопасный маршрутизатор для React, объединяющий дерево маршрутов, загрузку данных, интеграцию с suspense и SSR. Этот раздел поможет быстро подготовить проект к работе.

Требования

Установка пакетов

pnpm add @tanstack/react-router @tanstack/router-devtools
pnpm add -D @tanstack/router-plugin vite-tsconfig-paths

Дополнительные зависимости

Лайфхак: для песочницы или быстрого прототипа выполните 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"]
  }
}

Генерация дерева маршрутов

  1. Подключите Vite-плагин:
    // vite.config.ts
    import { TanStackRouterVite } from '@tanstack/router-plugin/vite'
    
    export default defineConfig({
      plugins: [react(), TanStackRouterVite()],
    })
    
  2. Проект автоматически создает routeTree.gen.ts. Добавьте файл в .gitignore или коммитьте, если CICD без генерации.
  3. Перед деплоем запускайте pnpm tanstack codegen && pnpm test — CI поймает несоответствие маршрутов типам до попадания в main.

Полезные опции плагина:

Настройка devtools

import { RouterDevtools } from '@tanstack/router-devtools'

<RouterProvider router={router}>
  <RouterDevtools initialIsOpen={false} position="bottom-right" />
</RouterProvider>

Проверка работоспособности

Полезные команды

| Команда | Описание | | — | — | | pnpm tanstack start | Запуск плагина, генерирующего дерево маршрутов. | | pnpm tanstack lint | Проверка схем путей/поиска. | | pnpm tanstack codegen | Одноразовая генерация routeTree.gen.ts. | | pnpm tanstack routes:inspect | Быстрый список маршрутов с параметрами и meta. | | pnpm tanstack routes:types | Регенерация только типов без перезаписи дерева (удобно в CI). |

Чек-лист

Советы по структуре маршрутов

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') }),
})

Загрузчики, actions и контекст

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 } })
  },
})

Интеграция с TanStack Query

Работа с 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),
})

Управление состоянием и навигацией

SSR, RSC и hybrid-рендеринг

Отладка и профилирование

Типовые сценарии и примеры

export const Route = createRoute({
  path: '/settings',
  component: SettingsPage,
  errorComponent: SettingsError,
  pendingComponent: () => <Spinner />,
  onLoad: ({ preload }) => preload({ select: (s) => s.location }),
})

Чек-лист перед релизом