React Router v6 - основы

16 июня 2026
Автор

Олег Марков

React Router v6 - основы

React Router — самая популярная библиотека для реализации маршрутизации в React-приложениях. Версия 6 была полностью переработана: появился более простой API, улучшена работа с вложенными маршрутами, добавлены новые хуки. В этой статье разберём всё, что нужно знать для начала работы с React Router v6.

Установка

npm install react-router-dom
# или
yarn add react-router-dom
# или
pnpm add react-router-dom

Для v6 нужен React 16.8 или новее (hooks support).

Базовая настройка

BrowserRouter

Самый простой способ начать — обернуть приложение в BrowserRouter:

import { BrowserRouter, Routes, Route } from 'react-router-dom';

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<HomePage />} />
        <Route path="/about" element={<AboutPage />} />
        <Route path="/users" element={<UsersPage />} />
        <Route path="*" element={<NotFoundPage />} />
      </Routes>
    </BrowserRouter>
  );
}

Отличия от v5

// React Router v5 — устаревший синтаксис
import { Switch, Route } from 'react-router-dom';

<Switch>
  <Route exact path="/" component={Home} />
  <Route path="/about" component={About} />
  <Route component={NotFound} />
</Switch>

// React Router v6 — новый синтаксис
import { Routes, Route } from 'react-router-dom';

<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/about" element={<About />} />
  <Route path="*" element={<NotFound />} />
</Routes>

Ключевые изменения:

  • SwitchRoutes
  • component={Home}element={<Home />}
  • exact больше не нужен — маршруты точные по умолчанию
  • <Route component={NotFound}><Route path="*" element={<NotFound />}>

Навигация

import { Link, NavLink } from 'react-router-dom';

function Navigation() {
  return (
    <nav>
      {/* Обычная ссылка */}
      <Link to="/">Главная</Link>
      <Link to="/about">О нас</Link>

      {/* NavLink добавляет класс active к активному маршруту */}
      <NavLink
        to="/users"
        className={({ isActive }) => (isActive ? 'nav-link active' : 'nav-link')}
      >
        Пользователи
      </NavLink>

      {/* Стилизация активной ссылки через style */}
      <NavLink
        to="/settings"
        style={({ isActive }) => ({
          color: isActive ? 'blue' : 'gray',
          fontWeight: isActive ? 'bold' : 'normal',
        })}
      >
        Настройки
      </NavLink>
    </nav>
  );
}

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

import { useNavigate } from 'react-router-dom';

function LoginForm() {
  const navigate = useNavigate();

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    try {
      await login(credentials);
      // Переход на главную после логина
      navigate('/dashboard');

      // Замена в истории (нельзя вернуться назад)
      navigate('/dashboard', { replace: true });

      // Переход с передачей состояния
      navigate('/checkout', { state: { from: '/cart' } });
    } catch (error) {
      navigate('/login?error=1');
    }
  };

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

function BackButton() {
  const navigate = useNavigate();

  return (
    <button onClick={() => navigate(-1)}>← Назад</button>
  );
}

Параметры маршрута

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

// Определяем маршрут с параметром
<Route path="/users/:userId" element={<UserProfile />} />
<Route path="/posts/:postId/comments/:commentId" element={<Comment />} />
import { useParams } from 'react-router-dom';

function UserProfile() {
  const { userId } = useParams<{ userId: string }>();

  return <h1>Профиль пользователя #{userId}</h1>;
}

function Comment() {
  const { postId, commentId } = useParams<{
    postId: string;
    commentId: string;
  }>();

  return <p>Комментарий {commentId} к посту {postId}</p>;
}

Query-параметры с useSearchParams

import { useSearchParams } from 'react-router-dom';

function ProductsList() {
  const [searchParams, setSearchParams] = useSearchParams();

  // Читаем параметры из URL: /products?category=electronics&sort=price&page=2
  const category = searchParams.get('category') ?? 'all';
  const sort = searchParams.get('sort') ?? 'date';
  const page = Number(searchParams.get('page') ?? '1');

  const handleCategoryChange = (newCategory: string) => {
    setSearchParams((prev) => {
      prev.set('category', newCategory);
      prev.set('page', '1'); // Сбрасываем страницу при смене категории
      return prev;
    });
  };

  const handleSortChange = (newSort: string) => {
    setSearchParams((prev) => {
      prev.set('sort', newSort);
      return prev;
    });
  };

  return (
    <div>
      <select value={category} onChange={(e) => handleCategoryChange(e.target.value)}>
        <option value="all">Все</option>
        <option value="electronics">Электроника</option>
        <option value="clothing">Одежда</option>
      </select>

      <select value={sort} onChange={(e) => handleSortChange(e.target.value)}>
        <option value="date">По дате</option>
        <option value="price">По цене</option>
        <option value="rating">По рейтингу</option>
      </select>

      <p>Страница {page}</p>
    </div>
  );
}

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

Outlet — одно из главных нововведений v6. Он позволяет рендерить дочерние маршруты внутри родительского компонента.

// Определяем структуру с вложенными маршрутами
function App() {
  return (
    <BrowserRouter>
      <Routes>
        {/* Layout-маршрут */}
        <Route path="/" element={<Layout />}>
          <Route index element={<HomePage />} />        {/* / */}
          <Route path="about" element={<AboutPage />} />  {/* /about */}

          {/* Вложенные маршруты пользователей */}
          <Route path="users" element={<UsersLayout />}>
            <Route index element={<UsersList />} />           {/* /users */}
            <Route path=":userId" element={<UserProfile />} />  {/* /users/123 */}
            <Route path=":userId/edit" element={<EditUser />} /> {/* /users/123/edit */}
          </Route>

          <Route path="settings" element={<SettingsPage />} />
        </Route>

        <Route path="*" element={<NotFoundPage />} />
      </Routes>
    </BrowserRouter>
  );
}
// Layout рендерит общие части и Outlet для дочерних маршрутов
function Layout() {
  return (
    <div>
      <Header />
      <nav>
        <NavLink to="/">Главная</NavLink>
        <NavLink to="/about">О нас</NavLink>
        <NavLink to="/users">Пользователи</NavLink>
      </nav>

      <main>
        {/* Здесь рендерится текущий дочерний маршрут */}
        <Outlet />
      </main>

      <Footer />
    </div>
  );
}

// UsersLayout — свой лейаут для раздела пользователей
function UsersLayout() {
  return (
    <div>
      <h1>Управление пользователями</h1>
      <Outlet /> {/* Рендерит UsersList или UserProfile */}
    </div>
  );
}

Передача данных в Outlet через context

function UsersLayout() {
  const [selectedUser, setSelectedUser] = useState(null);

  return (
    <div>
      <Outlet context={{ selectedUser, setSelectedUser }} />
    </div>
  );
}

// В дочернем компоненте
import { useOutletContext } from 'react-router-dom';

function UserProfile() {
  const { selectedUser, setSelectedUser } = useOutletContext();
  // ...
}

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

Защищённые маршруты — стандартный паттерн для ограничения доступа неавторизованным пользователям:

import { Navigate, useLocation } from 'react-router-dom';

// Компонент-обёртка для защищённых маршрутов
function RequireAuth({ children }: { children: JSX.Element }) {
  const { isAuthenticated } = useAuth();
  const location = useLocation();

  if (!isAuthenticated) {
    // Перенаправляем на логин, сохраняя текущий путь для редиректа после входа
    return <Navigate to="/login" state={{ from: location }} replace />;
  }

  return children;
}

// Обёртка для проверки роли
function RequireRole({
  children,
  role,
}: {
  children: JSX.Element;
  role: string;
}) {
  const { user } = useAuth();

  if (!user || user.role !== role) {
    return <Navigate to="/forbidden" replace />;
  }

  return children;
}

// Использование в маршрутах
function App() {
  return (
    <Routes>
      <Route path="/login" element={<LoginPage />} />
      <Route path="/forbidden" element={<ForbiddenPage />} />

      {/* Защищённые маршруты */}
      <Route
        path="/dashboard"
        element={
          <RequireAuth>
            <DashboardPage />
          </RequireAuth>
        }
      />

      {/* Группа защищённых маршрутов */}
      <Route
        element={
          <RequireAuth>
            <Layout />
          </RequireAuth>
        }
      >
        <Route path="/profile" element={<ProfilePage />} />
        <Route path="/settings" element={<SettingsPage />} />

        {/* Только для администраторов */}
        <Route
          path="/admin"
          element={
            <RequireRole role="admin">
              <AdminPanel />
            </RequireRole>
          }
        />
      </Route>
    </Routes>
  );
}
// LoginPage — перенаправляем после логина
function LoginPage() {
  const navigate = useNavigate();
  const location = useLocation();

  const from = (location.state as { from?: Location })?.from?.pathname ?? '/dashboard';

  const handleLogin = async (credentials) => {
    await login(credentials);
    navigate(from, { replace: true }); // Возвращаем туда, откуда пришли
  };

  return <LoginForm onSubmit={handleLogin} />;
}

useLocation и useMatch

import { useLocation, useMatch } from 'react-router-dom';

function BreadCrumbs() {
  const location = useLocation();

  // location.pathname — текущий путь
  // location.search — строка запроса (?key=value)
  // location.state — переданное состояние
  // location.hash — хэш (#section)

  return (
    <nav aria-label="breadcrumb">
      <span>Вы здесь: {location.pathname}</span>
    </nav>
  );
}

function ActiveSection() {
  // useMatch проверяет соответствие текущего пути шаблону
  const isUsersPage = useMatch('/users/*');
  const isUserProfile = useMatch('/users/:id');

  return (
    <div>
      {isUsersPage && <p>Вы в разделе пользователей</p>}
      {isUserProfile && <p>Просматриваете профиль #{isUserProfile.params.id}</p>}
    </div>
  );
}

Lazy loading маршрутов

Для оптимизации загрузки — разделение кода по маршрутам:

import { lazy, Suspense } from 'react';
import { Routes, Route, BrowserRouter } from 'react-router-dom';

// Ленивая загрузка компонентов страниц
const HomePage = lazy(() => import('./pages/HomePage'));
const AboutPage = lazy(() => import('./pages/AboutPage'));
const UsersPage = lazy(() => import('./pages/UsersPage'));
const AdminPanel = lazy(() => import('./pages/AdminPanel'));

function App() {
  return (
    <BrowserRouter>
      <Suspense fallback={<PageLoader />}>
        <Routes>
          <Route path="/" element={<HomePage />} />
          <Route path="/about" element={<AboutPage />} />
          <Route path="/users/*" element={<UsersPage />} />
          <Route
            path="/admin/*"
            element={
              <RequireAuth>
                <AdminPanel />
              </RequireAuth>
            }
          />
          <Route path="*" element={<NotFoundPage />} />
        </Routes>
      </Suspense>
    </BrowserRouter>
  );
}

function PageLoader() {
  return (
    <div className="page-loader">
      <Spinner />
    </div>
  );
}

createBrowserRouter и Data API

React Router v6.4+ добавил Data API — loaders и actions для загрузки данных прямо в маршрутах:

import { createBrowserRouter, RouterProvider } from 'react-router-dom';

// Создаём роутер с loaders
const router = createBrowserRouter([
  {
    path: '/',
    element: <Layout />,
    children: [
      {
        index: true,
        element: <HomePage />,
      },
      {
        path: 'users',
        element: <UsersPage />,
        // Загружается ДО рендера компонента
        loader: async () => {
          const response = await fetch('/api/users');
          if (!response.ok) throw new Response('Not Found', { status: 404 });
          return response.json();
        },
      },
      {
        path: 'users/:userId',
        element: <UserProfile />,
        loader: async ({ params }) => {
          const response = await fetch(`/api/users/${params.userId}`);
          if (!response.ok) throw new Response('Not Found', { status: 404 });
          return response.json();
        },
        // action для POST/PUT/DELETE
        action: async ({ request, params }) => {
          const formData = await request.formData();
          const updates = Object.fromEntries(formData);
          await updateUser(params.userId, updates);
          return redirect(`/users/${params.userId}`);
        },
      },
    ],
  },
]);

function App() {
  return <RouterProvider router={router} />;
}
// В компоненте — данные из loader доступны через useLoaderData
import { useLoaderData, Form } from 'react-router-dom';

function UsersPage() {
  const users = useLoaderData() as User[];

  return (
    <ul>
      {users.map((user) => (
        <li key={user.id}>
          <Link to={`/users/${user.id}`}>{user.name}</Link>
        </li>
      ))}
    </ul>
  );
}

function UserProfile() {
  const user = useLoaderData() as User;

  return (
    <div>
      <h1>{user.name}</h1>
      {/* Form автоматически отправляет данные в action */}
      <Form method="post">
        <input name="name" defaultValue={user.name} />
        <button type="submit">Сохранить</button>
      </Form>
    </div>
  );
}

Переходы и pending состояния

import { useNavigation } from 'react-router-dom';

function Layout() {
  const navigation = useNavigation();

  return (
    <div>
      {/* Показываем индикатор при переходе между страницами */}
      {navigation.state === 'loading' && <GlobalSpinner />}

      <Outlet />
    </div>
  );
}

Типичная структура маршрутов

Пример организации маршрутов в реальном приложении:

const router = createBrowserRouter([
  // Публичные страницы
  {
    path: '/login',
    element: <LoginPage />,
  },
  {
    path: '/register',
    element: <RegisterPage />,
  },

  // Основное приложение (авторизованные)
  {
    path: '/',
    element: <RequireAuth><AppLayout /></RequireAuth>,
    children: [
      { index: true, element: <Navigate to="/dashboard" replace /> },
      {
        path: 'dashboard',
        element: <DashboardPage />,
        loader: dashboardLoader,
      },
      {
        path: 'profile',
        element: <ProfilePage />,
        loader: profileLoader,
        action: profileAction,
      },

      // Раздел продуктов
      {
        path: 'products',
        element: <ProductsLayout />,
        children: [
          { index: true, element: <ProductsList />, loader: productsLoader },
          { path: ':id', element: <ProductDetail />, loader: productLoader },
        ],
      },

      // Только для администраторов
      {
        path: 'admin',
        element: <RequireRole role="admin"><AdminLayout /></RequireRole>,
        children: [
          { index: true, element: <AdminDashboard /> },
          { path: 'users', element: <AdminUsers />, loader: adminUsersLoader },
        ],
      },
    ],
  },

  // 404
  { path: '*', element: <NotFoundPage /> },
]);

Заключение

React Router v6 существенно упростил работу с маршрутизацией в React:

  • <Routes> и <Route> — декларативный, лаконичный синтаксис
  • <Outlet> — элегантное решение для вложенных маршрутов и общих лейаутов
  • useNavigate — программная навигация без устаревшего history
  • useParams и useSearchParams — удобный доступ к параметрам
  • Data API (loaders/actions) — загрузка данных прямо в маршрутах
  • Lazy loading через React.lazy — разделение кода без лишней настройки

React Router v6 стал стандартом маршрутизации в React-экосистеме. Изучив эти основы, вы сможете организовать навигацию любой сложности в своём приложении.

Стрелочка влевоЧто такое Vite и как запускать проекты React через негоКак использовать Next.js вместе с ReactСтрелочка вправо

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

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

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

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

Все гайды по React

Uncontrolled Components: когда DOM управляет даннымиБезопасность в React: защита от XSS, CSRF и утечек данныхRender Props: гибкое управление рендерингом в ReactРефакторинг 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 что это и как использоватьuseTransition - плавные переходы между состояниямиuseSyncExternalStore — работа с внешними сторамиuseRef в React — создание ссылок на DOM и значенияuseOptimistic — оптимистичные обновления UIuseLayoutEffect в React — эффект до отрисовкиuseInsertionEffect — внедрение стилей до мутаций DOMuseImperativeHandle в React — настройка ref дочернего компонентаuseId — генерация уникальных идентификаторовuseFormStatus - отслеживание статуса отправки формыuseDeferredValue — отложенное обновление состоянияuseDebugValue — отладка кастомных хуковuseCallback в React — мемоизация функцийuseReducer — альтернатива useState для сложной логикиuseMemo в React: как и когда оптимизировать тяжелые вычисленияuseEffect в React что это и как использоватьuseContext — работа с контекстом в ReactuseCallback в React — мемоизация функций и оптимизация ре-рендеровuseActionState в React 19Оптимизация рендеринга в React: от теории к глубокой практикеЧто такое useRef и как его применять в ReactКак и зачем использовать React HooksУправление состоянием в React через ContextКак предотвратить лишние ре-рендеры в React: полное руководствоuseMemo vs useCallback: подробное руководство по мемоизации в ReactПравила хуков — правила использованияuseEffect vs useLayoutEffect: в чём разница и какой хук выбрать?Кастомные хуки в React — создание собственных хуковuseState продвинутое использование в React
Transition API — плавные обновления интерфейса в ReactReact Suspense — приостановка рендераStrictMode в React — как находить ошибки на этапе разработкиСерверные компоненты React (RSC) — подробный разбор и практикаКак работает рендеринг в ReactЧто такое props в React и как их правильно использоватьКак работает JSX связка React и HTMLЧто такое React.js и как его использоватьКак использовать элементы в ReactКак использовать React DOM в проектеРабота с children в ReactПорталы в React: рендер компонентов вне иерархии DOMЧто такое компоненты в React и как их применятьFragment в 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 ₽
Подробнее

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