React с TanStack Router

10 сентября 2026
Автор

Антон Ларичев

Что такое TanStack Router

TanStack Router — это современная библиотека маршрутизации для React с полной поддержкой TypeScript из коробки. В отличие от React Router, она предоставляет 100% типобезопасность: параметры пути, строка запроса, данные загрузчиков — всё проверяется на уровне типов без дополнительных усилий.

Ключевые особенности:

  • Полная типобезопасность без ручных аннотаций
  • Встроенное кэширование данных загрузчиков (loader)
  • Типизированные параметры поиска (search params)
  • Поддержка вложенных (nested) маршрутов
  • Встроенный devtools для отладки
  • Поддержка серверного рендеринга (SSR)

Курс по теме

Курс React и Redux Toolkit с нуля

40 000+ студентов · рейтинг 4.8 · гарантия возврата 30 дней

Установка и начальная настройка

Установите библиотеку:

npm install @tanstack/react-router

Для автоматической генерации типов маршрутов установите плагин для Vite:

npm install -D @tanstack/router-plugin

Настройте vite.config.ts:

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { TanStackRouterVite } from '@tanstack/router-plugin/vite'

export default defineConfig({
  plugins: [
    TanStackRouterVite(),
    react(),
  ],
})

Плагин будет автоматически генерировать файл src/routeTree.gen.ts при каждом изменении файлов маршрутов.

Структура файлов маршрутов

TanStack Router использует файловую систему для определения маршрутов. Создайте директорию src/routes/:

src/
  routes/
    __root.tsx        # корневой макет
    index.tsx         # маршрут /
    about.tsx         # маршрут /about
    posts/
      index.tsx       # маршрут /posts
      $postId.tsx     # маршрут /posts/:postId
  main.tsx
  routeTree.gen.ts    # генерируется автоматически

Корневой маршрут

Файл src/routes/__root.tsx — это обёртка для всего приложения:

import { createRootRoute, Link, Outlet } from '@tanstack/react-router'
import { TanStackRouterDevtools } from '@tanstack/router-devtools'

export const Route = createRootRoute({
  component: () => (
    <>
      <nav>
        <Link to="/" activeProps={{ className: 'active' }}>
          Главная
        </Link>
        <Link to="/about" activeProps={{ className: 'active' }}>
          О нас
        </Link>
        <Link to="/posts" activeProps={{ className: 'active' }}>
          Посты
        </Link>
      </nav>
      <Outlet />
      <TanStackRouterDevtools />
    </>
  ),
})

<Outlet /> — место, куда рендерятся дочерние маршруты.

Основные маршруты

Файл src/routes/index.tsx:

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

export const Route = createFileRoute('/')({ 
  component: HomePage,
})

function HomePage() {
  return <h1>Добро пожаловать</h1>
}

Файл src/routes/about.tsx:

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

export const Route = createFileRoute('/about')({
  component: AboutPage,
})

function AboutPage() {
  return <h1>О нас</h1>
}

Инициализация роутера

Файл 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>
)

Объявление модуля Register даёт TypeScript знание о конкретном экземпляре роутера — это активирует полную типобезопасность по всему приложению.

Динамические параметры пути

Файл с символом $ в имени становится динамическим маршрутом. Файл src/routes/posts/$postId.tsx:

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

export const Route = createFileRoute('/posts/$postId')({
  loader: async ({ params }) => {
    const response = await fetch(`https://api.example.com/posts/${params.postId}`)
    if (!response.ok) throw new Error('Пост не найден')
    return response.json()
  },
  component: PostPage,
})

function PostPage() {
  const post = Route.useLoaderData()
  const { postId } = Route.useParams()

  return (
    <article>
      <h1>{post.title}</h1>
      <p>ID: {postId}</p>
      <p>{post.body}</p>
    </article>
  )
}

params.postId здесь имеет тип string — TypeScript автоматически выводит его из имени файла $postId.tsx.

Типизированные параметры поиска

Одна из мощнейших возможностей TanStack Router — типизированная строка запроса. Обычные библиотеки работают с URLSearchParams вручную, здесь же всё проверяется компилятором.

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

const searchSchema = z.object({
  page: z.number().default(1),
  filter: z.string().optional(),
  sort: z.enum(['asc', 'desc']).default('asc'),
})

export const Route = createFileRoute('/posts/')({  
  validateSearch: searchSchema,
  component: PostsPage,
})

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

  return (
    <div>
      <p>Страница: {page} | Сортировка: {sort}</p>
      <button
        onClick={() => navigate({ search: { page: page + 1, sort } })
      }>
        Следующая страница
      </button>
      <input
        value={filter ?? ''}
        onChange={(e) =>
          navigate({ search: { page: 1, sort, filter: e.target.value } })
        }
        placeholder="Поиск..."
      />
    </div>
  )
}

Параметр page всегда будет number, filterstring | undefined, а sort — только 'asc' или 'desc'. Любое несоответствие вызовет ошибку TypeScript ещё до запуска кода.

Загрузчики данных (Loaders)

Loader — функция, которая выполняется перед рендером компонента. Данные из неё доступны через Route.useLoaderData() без дополнительных состояний или useEffect.

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

interface Post {
  id: number
  title: string
  body: string
}

export const Route = createFileRoute('/posts/')({ 
  loader: async (): Promise<Post[]> => {
    const response = await fetch('https://jsonplaceholder.typicode.com/posts')
    return response.json()
  },
  component: PostsListPage,
  pendingComponent: () => <div>Загрузка постов...</div>,
  errorComponent: ({ error }) => <div>Ошибка: {error.message}</div>,
})

function PostsListPage() {
  const posts = Route.useLoaderData()

  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>
          <Link to="/posts/$postId" params={{ postId: String(post.id) }}>
            {post.title}
          </Link>
        </li>
      ))}
    </ul>
  )
}

pendingComponent отображается пока данные загружаются, errorComponent — при ошибке. Роутер сам управляет этими состояниями.

Программная навигация

Для навигации из кода используйте хук useNavigate:

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

function LoginForm() {
  const navigate = useNavigate()

  const handleSubmit = async (credentials: Credentials) => {
    await login(credentials)
    navigate({ to: '/dashboard', replace: true })
  }

  return <form onSubmit={handleSubmit}>...</form>
}

Парамтр to полностью типизирован — автодополнение подскажет все доступные маршруты.

Защищённые маршруты

Для защиты маршрутов используйте beforeLoad — функцию, которая выполняется перед загрузкой маршрута:

import { createFileRoute, redirect } from '@tanstack/react-router'
import { getAuthSession } from '../lib/auth'

export const Route = createFileRoute('/dashboard')({ 
  beforeLoad: async ({ location }) => {
    const session = await getAuthSession()
    if (!session) {
      throw redirect({
        to: '/login',
        search: {
          redirect: location.href,
        },
      })
    }
    return { session }
  },
  loader: async ({ context }) => {
    const { session } = context
    return fetchDashboardData(session.userId)
  },
  component: DashboardPage,
})

Данные, возвращённые из beforeLoad, попадают в context и доступны в loader и компоненте.

Общий контекст аутентификации через корневой маршрут

Чтобы не повторять проверку в каждом маршруте, вынесите её в __root.tsx:

import { createRootRouteWithContext } from '@tanstack/react-router'

interface RouterContext {
  auth: AuthContext
}

export const Route = createRootRouteWithContext<RouterContext>()({
  component: RootLayout,
})

Передайте контекст при создании роутера в main.tsx:

const auth = useAuth()

const router = createRouter({
  routeTree,
  context: { auth },
})

Теперь в любом beforeLoad или loader будет доступен context.auth.

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

function Navigation() {
  return (
    <nav>
      <Link
        to="/posts/$postId"
        params={{ postId: '42' }}
        search={{ page: 1, sort: 'desc' }}
        activeProps={{ className: 'nav-link--active' }}
        inactiveProps={{ className: 'nav-link' }}
      >
        Пост #42
      </Link>
    </nav>
  )
}

activeProps применяются, когда текущий URL совпадает с маршрутом ссылки. Все параметры (params, search) проверяются TypeScript на корректность.

Вложенные маршруты и макеты

Вложенность создаётся через директории. Файл src/routes/dashboard.tsx будет родителем для всего внутри src/routes/dashboard/:

// src/routes/dashboard.tsx
import { createFileRoute, Outlet } from '@tanstack/react-router'

export const Route = createFileRoute('/dashboard')({
  component: () => (
    <div className="dashboard-layout">
      <aside>
        <Link to="/dashboard/profile">Профиль</Link>
        <Link to="/dashboard/settings">Настройки</Link>
      </aside>
      <main>
        <Outlet />
      </main>
    </div>
  ),
})
// src/routes/dashboard/profile.tsx
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/dashboard/profile')({
  component: () => <h1>Профиль пользователя</h1>,
})

Компонент dashboard.tsx будет обёрткой — боковая панель останется при переходах между /dashboard/profile и /dashboard/settings.

Devtools

Установите devtools для отладки:

npm install -D @tanstack/router-devtools

Добавьте в корневой маршрут:

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

export const Route = createRootRoute({
  component: () => (
    <>
      <Outlet />
      {import.meta.env.DEV && <TanStackRouterDevtools />}
    </>
  ),
})

Devtools показывают текущее дерево маршрутов, параметры, состояние загрузчиков и историю навигации.

Сравнение с React Router

Функция TanStack Router React Router v6
Типобезопасность параметров Полная Ручные аннотации
Типизированный search Встроен Ручная работа
Встроенные загрузчики Да Да (v6.4+)
Файловая маршрутизация Да Нет
Размер бандла ~13 KB ~15 KB
Кэширование данных Встроено Нет

TanStack Router особенно выигрывает в проектах на TypeScript, где типобезопасность навигации критична — например, в приложениях с большим количеством маршрутов и сложными параметрами.

Итог

TanStack Router предлагает принципиально другой уровень комфорта для разработчиков на TypeScript. Типобезопасные параметры пути и строки запроса, встроенные загрузчики с состояниями загрузки и ошибок, декларативная защита маршрутов через beforeLoad — всё это делает маршрутизацию надёжной частью архитектуры, а не источником runtime-ошибок.

Чтобы глубже освоить экосистему React — от базовых концепций до продвинутых паттернов — посмотрите курс по React на PurpleSchool: https://purpleschool.ru/course/react?utm_source=knowledgebase&utm_medium=text&utm_campaign=tanstack-router

Постройте личный план изучения React до уровня Middle — бесплатно!

React — часть карты развития Frontend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Бесплатные лекции

Все гайды по React

React тестирование с Vitest
React с TanStack Router
Uncontrolled Components: когда DOM управляет даннымиБезопасность в React: защита от XSS, CSRF и утечек данныхRender Props: гибкое управление рендерингом в ReactРефакторинг React-кода: техники и лучшие практикиReact с WebSocket — реалтайм в компонентахReact Native приложение с нуля до публикации в сторахПрофилирование React: как найти и устранить узкие местаЧастичное применение: как создавать компоненты без лишнего кодаИменование компонентов в React: соглашения и лучшие практикиЛенивая загрузка: как ускорить React-приложение в разыОшибки гидратации в React и способы их устраненияHOC в React: мастерство композиции компонентовuseMemo: как спасти производительность от тяжелых вычисленийError Boundaries: создаем надежные React-приложенияКонтролируемые компоненты в React: полный контроль над формамиCompound Components в React: создаем гибкие компоненты с мощным APIДокументирование компонентов в React: Storybook, JSDoc и READMEКомпозиция компонентов в React: строим гибкие интерфейсыКомментирование кода в React: когда и как писать комментарииCode Splitting в React: как уменьшить бандл и ускорить загрузку приложенияАсинхронные компоненты в React: новый стандарт работы с даннымиДоступность (a11y) в React: ARIA, семантика и клавиатурная навигация
Zustand — управление состоянием в ReactZod - валидация с TypeScriptYup - валидация схемXState - конечные автоматыТемизация в ReactТестирование хуковTailwind CSS с ReactSWR - библиотека для запросовStyled Components — стилизация через JSStorybook - документация компонентовSnapshots тестированиеRTK Query - работа с APIRedux Toolkit - современный ReduxRecoil — библиотека управления состоянием от FacebookВиртуализация списков с react-window: как отображать тысячи элементов без лаговReact Toastify - уведомления в ReactReact Testing LibraryСоздание таблиц в React гайд по react-tableReact Spring - анимацииРабота с формами и селектами в ReactReact Query (TanStack Query) - работа с серверомПлагины в React что это и как их использоватьReact PDF - работа с PDF файламиОбзор популярных библиотек для ReactReact Icons - библиотека иконок для ReactReact Hook Form — валидация форм в ReactReact Dropzone — загрузка файловПодключение Bootstrap к React-приложениюReact Beautiful DnD - перетаскивание элементовАнимация при монтировании компонентов в ReactМокирование APIMobX — реактивное управление состоянием в ReactМикрофронтенды с React (micro-frontends)Загрузка и индикаторыАнимация списков в ReactJotai - атомарное состояниеБесконечная прокруткаFramer Motion - библиотека анимацийEmotion — библиотека CSS-in-JSДинамические стили в ReactE2E тестирование с CypressCSSTransition - переходыCSS-in-JS — плюсы и минусыКонтекст vs Redux — когда что использоватьИспользование Chart.js в ReactAxios с ReactТестирование асинхронных компонентовОбработка ошибок API
useState в React что это и как использоватьuseSyncExternalStore — работа с внешними сторамиuseTransition - плавные переходы между состояниямиuseRef в React — создание ссылок на DOM и значенияuseOptimistic — оптимистичные обновления UIuseLayoutEffect в React — эффект до отрисовкиuseInsertionEffect — внедрение стилей до мутаций DOMuseImperativeHandle в React — настройка ref дочернего компонентаuseId — генерация уникальных идентификаторовuseFormStatus - отслеживание статуса отправки формыuseDeferredValue — отложенное обновление состоянияuseDebugValue — отладка кастомных хуковuseCallback в React — мемоизация функцийuseReducer — альтернатива useState для сложной логикиuseMemo в React: как и когда оптимизировать тяжелые вычисленияХук use() в React 19useEffect в React что это и как использоватьuseContext — работа с контекстом в ReactuseCallback в React — мемоизация функций и оптимизация ре-рендеровuseActionState в React 19Оптимизация рендеринга в React: от теории к глубокой практикеЧто такое useRef и как его применять в ReactКак и зачем использовать React HooksУправление состоянием в React через ContextНовые возможности React 19Как предотвратить лишние ре-рендеры в React: полное руководствоuseMemo vs useCallback: подробное руководство по мемоизации в ReactПравила хуков — правила использованияflushSync — синхронные обновления состояния в ReactuseEffect vs useLayoutEffect: в чём разница и какой хук выбрать?Кастомные хуки в React — создание собственных хуковuseState продвинутое использование в React
Transition API — плавные обновления интерфейса в ReactReact Suspense — приостановка рендераStrictMode в React — как находить ошибки на этапе разработкиСерверные компоненты React (RSC) — подробный разбор и практикаКак работает рендеринг в ReactЧто такое props в React и как их правильно использоватьКак работает JSX связка React и HTMLЧто такое React.js и как его использоватьКак использовать элементы в ReactКак использовать React DOM в проектеЧто такое компоненты в React и как их применятьРабота с children в ReactПорталы в React: рендер компонентов вне иерархии DOMFragment в React: группировка элементов без лишних узлов DOMCSS Modules в ReactConcurrent Mode — конкурентный режим в React
Открыть базу знаний

Лучшие курсы по теме

изображение курса

React и Redux Toolkit

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий