React Router DOM с нуля до продакшена: маршруты, защита, lazy loading и лучшие практики 2026
Когда React-приложение состоит из одной страницы, о маршрутизации можно почти не думать. Но как только появляются личный кабинет, каталог товаров, профиль пользователя или несколько разделов приложения, простой подход с условным рендерингом быстро превращается в проблему.
Представьте интернет-магазин на React. У пользователя открыт адрес:
/products/macbook/reviewsОн обновляет страницу и ожидает увидеть отзывы именно этого товара. Приложение должно понять, какой компонент показать, какие данные загрузить и какое состояние восстановить.
Именно эту задачу решает React Router DOM.
Это не просто библиотека для переключения страниц. В современном React-проекте маршрутизация становится частью архитектуры приложения: она определяет структуру URL, разделяет публичные и приватные разделы, управляет вложенными страницами и помогает загружать только тот код, который действительно нужен пользователю.
В этом руководстве разберём актуальный подход к React Router DOM v7 в 2026 году. Начнём с базовой настройки, а затем перейдём к архитектуре, которую можно использовать в реальных production-проектах.
Быстрый старт: первый React Router за несколько минут
Начнём с минимальной конфигурации. В реальном проекте маршруты обычно становятся сложнее, но фундамент остаётся тем же: есть URL, есть компонент, который должен отображаться, и есть правила доступа.
Установите библиотеку:
npm install react-router-domПосле установки создадим несколько страниц:
src/
├── pages/
│ ├── Home.jsx
│ ├── Login.jsx
│ └── Dashboard.jsx
└── App.jsxТеперь настроим маршруты:
import {
BrowserRouter,
Routes,
Route,
Navigate,
Outlet
} from "react-router-dom";
import {
lazy,
Suspense
} from "react";
const Home = lazy(() => import("./pages/Home"));
const Login = lazy(() => import("./pages/Login"));
const Dashboard = lazy(() => import("./pages/Dashboard"));
function ProtectedRoute() {
const token = localStorage.getItem("token");
return token
? <Outlet />
: <Navigate to="/login" replace />;
}
function App() {
return (
<BrowserRouter>
<Suspense fallback={<div>Загрузка...</div>}>
<Routes>
<Route
path="/"
element={<Home />}
/>
<Route
path="/login"
element={<Login />}
/>
<Route element={<ProtectedRoute />}>
<Route
path="/dashboard"
element={<Dashboard />}
/>
</Route>
<Route
path="*"
element={<h1>404</h1>}
/>
</Routes>
</Suspense>
</BrowserRouter>
);
}
export default App;Даже этот небольшой пример уже содержит основные концепции, которые используются в больших приложениях: маршруты, приватные страницы, обработку ошибок и ленивую загрузку.
Дальше разберём каждую часть подробнее.
Что такое React Router DOM
React Router DOM - это библиотека маршрутизации для React-приложений, которые работают в браузере.
Если объяснять просто, она связывает адрес в строке браузера с React-компонентом.
Например:
site.com/profileможет отображать:
<Profile />А:
site.com/products/25может открыть:
<ProductDetails />При этом браузер не загружает новую HTML-страницу, как это происходит на обычном сайте. React остаётся запущенным, а Router меняет только нужную часть интерфейса.
Именно поэтому SPA-приложения ощущаются быстрее: переход между разделами происходит практически мгновенно.
Почему нельзя просто использовать useState
Новички часто начинают с простого решения:
const [page, setPage] = useState("home");
if (page === "home") {
return <Home />;
}
if (page === "about") {
return <About />;
}Для небольшого эксперимента это нормально. Но у такого подхода есть фундаментальная проблема - URL никак не связан с состоянием приложения.
Пользователь не сможет:
сохранить страницу в закладки;
отправить ссылку другому человеку;
использовать кнопку «Назад» браузера;
открыть нужный экран после обновления страницы.
В production-приложении адрес должен быть источником состояния.
Если пользователь открыл:
/account/settingsReact должен восстановить именно этот экран, а не начинать приложение заново.
Как работает маршрутизация в React SPA
В классическом сайте каждый переход выглядит примерно так:
Браузер
↓
Запрос серверу
↓
Новый HTML
↓
Отрисовка страницыВ React-приложении схема другая:
Браузер
↓
React приложение
↓
React Router
↓
Подходящий компонентПосле первой загрузки приложение уже находится в памяти браузера. Когда пользователь нажимает ссылку, Router изменяет URL и показывает нужный компонент.
Например:
/dashboardпревращается в:
<Dashboard />А:
/users/42становится:
<UserProfile />с параметром:
42React Router и React Router DOM: что выбрать
У библиотеки есть несколько пакетов, и здесь часто возникает путаница.
react-router - это ядро. Оно содержит основную логику маршрутизации и не зависит от конкретной платформы.
react-router-dom предназначен для браузерных приложений. Именно он добавляет инструменты, которые используются в обычных React-сайтах:
BrowserRouter
Link
NavLinkПоэтому если вы создаёте:
сайт;
интернет-магазин;
админку;
SaaS-приложение;
вам нужен:
npm install react-router-domОтдельно устанавливать react-router обычно не требуется.
BrowserRouter - основа маршрутизации
После установки библиотеки приложение нужно подключить к Router.
Обычно это делается в main.jsx:
import React from "react";
import ReactDOM from "react-dom/client";
import {
BrowserRouter
} from "react-router-dom";
import App from "./App";
ReactDOM.createRoot(
document.getElementById("root")
)
.render(
<BrowserRouter>
<App />
</BrowserRouter>
);BrowserRouter использует встроенный History API браузера. Благодаря этому адрес меняется без полной перезагрузки страницы.
Например, переход:
/productsна:
/products/15не заставляет браузер заново загружать весь сайт.
Продолжаю в том же стиле: меньше «списков из документации», больше практического объяснения.
Routes и Route: создаём страницы приложения
После подключения BrowserRouter можно описать структуру приложения.
В React Router DOM за это отвечают два основных компонента:
Routes - контейнер, который ищет подходящий маршрут.
Route - конкретное правило: какой URL должен показывать какой компонент.
Например, у нас есть небольшой блог:
import {
Routes,
Route
} from "react-router-dom";
import Home from "./pages/Home";
import Article from "./pages/Article";
import About from "./pages/About";
function AppRoutes() {
return (
<Routes>
<Route
path="/"
element={<Home />}
/>
<Route
path="/article"
element={<Article />}
/>
<Route
path="/about"
element={<About />}
/>
</Routes>
);
}Теперь React понимает:
если пользователь открыл / - показать главную страницу;
если /article - загрузить компонент статьи;
если /about - показать информацию о проекте.
В старых версиях React Router часто использовали подход со Switch. Начиная с версии 6 используется Routes, а выбор маршрута происходит автоматически по наиболее подходящему совпадению.
Навигация через Link вместо обычного a
Одна из самых частых ошибок новичков - использовать обычные HTML-ссылки:
<a href="/profile">
Профиль
</a>На первый взгляд всё работает. Но браузер воспринимает такую ссылку как переход на новый сайт и полностью перезагружает приложение.
В SPA-приложениях используется компонент Link:
import {
Link
} from "react-router-dom";
function Menu() {
return (
<nav>
<Link to="/">
Главная
</Link>
<Link to="/articles">
Статьи
</Link>
<Link to="/profile">
Профиль
</Link>
</nav>
);
}Теперь переход происходит внутри React-приложения.
React Router изменяет URL, сохраняет состояние приложения и показывает новый компонент без полного обновления страницы.
NavLink: умное меню с активным состоянием
Для обычных ссылок достаточно Link.
Но в меню часто нужно понимать, какая страница сейчас открыта.
Например, пользователь находится в разделе:
/articlesи пункт меню «Статьи» должен стать активным.
Для этого существует NavLink.
import {
NavLink
} from "react-router-dom";
function Navigation() {
return (
<nav>
<NavLink
to="/"
>
Главная
</NavLink>
<NavLink
to="/articles"
className={({isActive}) =>
isActive
? "active"
: ""
}
>
Статьи
</NavLink>
</nav>
);
}Когда URL совпадает, React Router автоматически передаёт isActive.
Это особенно удобно для:
личных кабинетов;
панелей управления;
больших меню.
Динамические маршруты: страницы с параметрами
Практически любое реальное приложение работает с динамическими URL.
Например, интернет-магазин:
/products/iphone-15или блог:
/articles/react-router-guideМы не можем создавать отдельный маршрут для каждой статьи.
Вместо этого используем параметры.
<Route
path="/articles/:slug"
element={<Article />}
/>Теперь любой адрес:
/articles/react-router-guideпопадёт в компонент:
<Article />А значение react-router-guide можно получить через useParams.
import {
useParams
} from "react-router-dom";
function Article() {
const {
slug
} = useParams();
return (
<article>
<h1>
Статья: {slug}
</h1>
</article>
);
}В результате:
/articles/react-router-guideдаст:
slug = "react-router-guide"Так обычно строятся страницы товаров, профилей пользователей, документации и блогов.
Несколько параметров в одном маршруте
React Router позволяет создавать более сложные URL.
Например:
/category/javascript/article/react-routerНастройка:
<Route
path="/category/:category/article/:slug"
element={<Article />}
/>Получение данных:
const {
category,
slug
} = useParams();Теперь приложение знает:
category = "javascript"
slug = "react-router"Такой подход часто используется в CMS, интернет-магазинах и новостных проектах.
Вложенные маршруты: основа большой архитектуры
Когда приложение становится большим, страницы начинают иметь общие элементы.
Например, личный кабинет:
Dashboard
│
├── Главная
├── Пользователи
├── Настройки
└── АналитикаУ всех страниц есть одинаковые части:
верхняя панель;
боковое меню;
футер.
Не нужно копировать их в каждый компонент.
Для этого используются вложенные маршруты.
Создадим общий Layout:
import {
Outlet
} from "react-router-dom";
function DashboardLayout() {
return (
<div>
<header>
Админ-панель
</header>
<aside>
Меню
</aside>
<main>
<Outlet />
</main>
</div>
);
}Главный элемент здесь:
<Outlet />Это место, куда React Router вставит дочерний компонент.
Теперь создаём структуру:
<Routes>
<Route
path="/dashboard"
element={<DashboardLayout />}
>
<Route
index
element={<DashboardHome />}
/>
<Route
path="users"
element={<Users />}
/>
<Route
path="settings"
element={<Settings />}
/>
</Route>
</Routes>Теперь работают адреса:
/dashboardпоказывает:
DashboardLayout
+
DashboardHomeА:
/dashboard/usersпоказывает:
DashboardLayout
+
UsersПри этом общий интерфейс не дублируется.
Почему Outlet важен в production-проектах
Без вложенных маршрутов большой React-проект быстро превращается в набор копий:
<header />
<Sidebar />
<UserPage />
<footer />потом:
<header />
<Sidebar />
<SettingsPage />
<footer />и так десятки раз.
Outlet решает эту проблему архитектурно.
Один Layout управляет общей частью приложения, а маршруты отвечают только за свой контент.
Именно так обычно строятся:
CRM-системы;
SaaS-платформы;
административные панели;
большие интернет-магазины.
Что уже умеет наше приложение
На этом этапе у нас есть фундамент настоящего React-приложения:
маршруты между страницами;
переходы без перезагрузки;
динамические URL;
страницы с параметрами;
общие Layout-компоненты;
вложенная архитектура.
Следующий важный шаг - научиться закрывать страницы от посторонних пользователей.
Защита маршрутов: как сделать приватные страницы в React
В реальных приложениях далеко не все страницы должны быть доступны каждому пользователю.
Например, в блоге может быть открыта страница статьи, но личный кабинет автора должен видеть только пользователь после входа.
В интернет-магазине каталог доступен всем, а история заказов - только владельцу аккаунта.
Именно для таких сценариев используются Protected Routes.
Идея простая: перед тем как показать компонент, React Router проверяет состояние пользователя.
Схема выглядит так:
Пользователь открывает /dashboard
↓
Есть авторизация?
↙ ↘
Да Нет
↓ ↓
Dashboard LoginСоздаём ProtectedRoute
Начнём с простого варианта.
Создадим отдельный компонент:
import {
Navigate,
Outlet
} from "react-router-dom";
function ProtectedRoute() {
const token = localStorage.getItem("token");
if (!token) {
return (
<Navigate
to="/login"
replace
/>
);
}
return <Outlet />;
}
export default ProtectedRoute;Здесь происходит несколько вещей.
Navigate выполняет перенаправление пользователя.
Если токена нет, человек отправляется на страницу входа.
Outlet работает так же, как и во вложенных маршрутах. Он показывает дочерний компонент только после успешной проверки.
Подключаем защищённые маршруты
Теперь можно закрыть целый раздел приложения:
<Routes>
<Route
path="/login"
element={<Login />}
/>
<Route element={<ProtectedRoute />}>
<Route
path="/dashboard"
element={<Dashboard />}
/>
<Route
path="/profile"
element={<Profile />}
/>
</Route>
</Routes>Теперь:
/dashboardоступен только авторизованным пользователям.
Если пользователь не вошёл в систему, React Router автоматически отправит его:
/loginЗачем нужен replace в Navigate
В примере выше используется:
<Navigate
to="/login"
replace
/>Почему не просто:
<Navigate
to="/login"
/>Разница в истории браузера.
Без replace получится такая последовательность:
/dashboard
↓
/loginПользователь нажимает кнопку «Назад»:
/dashboardИ снова попадает на закрытую страницу.
С replace старая запись заменяется:
/dashboard
↓
/loginВ истории браузера уже нет предыдущего защищённого маршрута.
Более правильный вариант: проверка через состояние приложения
В небольшом примере достаточно localStorage.
Но в реальном проекте обычно есть глобальное состояние:
Redux Toolkit;
Zustand;
Context API;
серверная сессия.
Например:
import {
useSelector
} from "react-redux";
function ProtectedRoute() {
const user = useSelector(
state => state.auth.user
);
return user
? <Outlet />
: <Navigate to="/login" replace />;
}Теперь маршрутизация зависит не от локального хранилища, а от состояния всего приложения.
Это удобнее, когда есть:
роли пользователей;
права доступа;
обновление профиля;
выход из аккаунта.
Защита маршрутов по ролям
Иногда недостаточно просто проверить вход.
Например:
обычный пользователь видит профиль;
редактор может создавать статьи;
администратор управляет пользователями.
Тогда проверяем роль.
function ProtectedRoute({ role }) {
const user = useAuth();
if (!user) {
return (
<Navigate to="/login" />
);
}
if (user.role !== role) {
return (
<Navigate to="/403" />
);
}
return <Outlet />;
}Использование:
<Route
element={
<ProtectedRoute role="admin" />
}
>
<Route
path="/admin"
element={<AdminPanel />}
/>
</Route>Теперь раздел администратора закрыт даже для пользователей с обычным аккаунтом.
Частая ошибка: считать ProtectedRoute полноценной защитой
Важно понимать один момент.
Protected Route защищает интерфейс.
Он не заменяет безопасность сервера.
Например, если пользователь видит кнопку:
Удалить статьюи она скрыта через React Router - это ещё не значит, что данные защищены.
Настоящая проверка должна происходить на backend:
React
↓
API запрос
↓
Сервер проверяет права
↓
ОтветПравильная архитектура выглядит так:
Frontend отвечает за удобство пользователя.
Backend отвечает за безопасность данных.
Lazy loading: ускоряем загрузку React-приложения
Когда приложение маленькое, весь JavaScript-код может загружаться сразу.
Например:
Главная
+
Каталог
+
Личный кабинет
+
Админка
+
Редактор
+
НастройкиДаже если пользователь открыл только главную страницу, браузер скачивает всё приложение.
В больших проектах это становится проблемой.
Решение - lazy loading.
Идея простая:
компонент загружается только тогда, когда пользователь действительно переходит на страницу.
React.lazy и Suspense
Вместо обычного импорта:
import Dashboard from "./pages/Dashboard";используем:
import {
lazy
} from "react";
const Dashboard = lazy(
() => import("./pages/Dashboard")
);Теперь Dashboard попадёт в отдельный JavaScript-файл.
Когда пользователь откроет:
/dashboardReact загрузит этот код.
Добавляем Suspense
Поскольку компонент теперь загружается асинхронно, нужен экран ожидания:
import {
Suspense
} from "react";
<Suspense
fallback={
<div>
Загрузка страницы...
</div>
}
>
<Routes>
<Route
path="/dashboard"
element={<Dashboard />}
/>
</Routes>
</Suspense>Пока браузер получает компонент, пользователь видит fallback.
В реальных проектах вместо текста обычно используют:
skeleton-загрузку;
анимацию;
прогресс-бар.
Где использовать lazy loading
Не нужно делать ленивым абсолютно всё.
Например, главный экран обычно должен открываться максимально быстро.
А вот страницы:
/dashboard
/admin
/editor
/settings
/analyticsпочти всегда стоит загружать отдельно.
Особенно это важно для больших приложений, где первоначальный JavaScript-бандл может занимать несколько мегабайт.
Lazy loading вместе с маршрутами
Чаще всего в production используют такую схему:
import {
lazy,
Suspense
} from "react";
const Home = lazy(
() => import("./pages/Home")
);
const Dashboard = lazy(
() => import("./pages/Dashboard")
);
function RoutesConfig() {
return (
<Suspense
fallback={<Loading />}
>
<Routes>
<Route
path="/"
element={<Home />}
/>
<Route
path="/dashboard"
element={<Dashboard />}
/>
</Routes>
</Suspense>
);
}Получается:
пользователь открывает главную страницу → загружается только Home.
Переходит в кабинет → React подгружает Dashboard.
Хуки React Router DOM: управление навигацией и URL
Когда приложение становится сложнее, простых ссылок через Link уже недостаточно.
Например:
после успешной авторизации нужно автоматически отправить пользователя в личный кабинет;
после сохранения формы нужно вернуть его на страницу списка;
нужно получить параметры текущего URL;
нужно синхронизировать фильтры с адресной строкой.
Для этого React Router DOM предоставляет специальные хуки.
useNavigate: переходы через код
useNavigate используется, когда переход должен произойти не по клику на ссылку, а после какого-то действия.
Самый распространённый пример - авторизация.
Пользователь вводит логин и пароль:
import {
useNavigate
} from "react-router-dom";
function Login() {
const navigate = useNavigate();
async function handleLogin() {
await loginUser();
navigate("/dashboard");
}
return (
<button onClick={handleLogin}>
Войти
</button>
);
}После успешного входа пользователь автоматически попадает в личный кабинет.
navigate также умеет работать с историей браузера.
Например:
navigate(-1);вернёт пользователя на предыдущую страницу.
Это аналог кнопки «Назад».
А:
navigate(1);перейдёт вперёд по истории.
Можно передавать состояние между страницами:
navigate(
"/profile",
{
state: {
message: "Добро пожаловать!"
}
}
);Получить данные можно через useLocation.
useLocation: работа с текущим URL
useLocation возвращает информацию о текущем маршруте.
Например:
import {
useLocation
} from "react-router-dom";
function PageInfo() {
const location = useLocation();
console.log(
location.pathname
);
}Если пользователь находится на:
/articles/react-routerполучим:
location.pathname
"/articles/react-router"Один из практических сценариев - создание хлебных крошек.
Например:
function Breadcrumbs() {
const {
pathname
} = useLocation();
const parts =
pathname
.split("/")
.filter(Boolean);
return (
<div>
{parts.map(part => (
<span key={part}>
{part}
</span>
))}
</div>
);
}Для адреса:
/blog/react-routerполучим:
blog → react-routeruseParams: получение данных из URL
Мы уже использовали динамические маршруты:
<Route
path="/article/:slug"
element={<Article />}
/>Теперь получим значение:
import {
useParams
} from "react-router-dom";
function Article() {
const {
slug
} = useParams();
return (
<h1>
{slug}
</h1>
);
}Если URL:
/article/react-router-guideкомпонент получит:
slug = "react-router-guide"Так обычно работают:
страницы статей;
карточки товаров;
профили пользователей;
документация.
useSearchParams: работа с параметрами поиска
Не все данные должны быть частью пути.
Например:
/products?page=2&category=phonesЗдесь используются query-параметры.
Для них есть:
useSearchParamsПример:
import {
useSearchParams
} from "react-router-dom";
function Products() {
const [
searchParams,
setSearchParams
] = useSearchParams();
const page =
searchParams.get("page") || "1";
return (
<button
onClick={() =>
setSearchParams({
page: "2"
})
}
>
Страница {page}
</button>
);
}Теперь состояние страницы хранится прямо в URL.
Это особенно полезно для:
фильтров;
поиска;
сортировки;
пагинации.
Например:
/articles?tag=react&page=3можно сохранить, отправить другу или добавить в закладки.
Правильная структура React Router проекта
В маленьком приложении можно держать всё в App.jsx.
Но когда проект растёт, маршруты лучше вынести отдельно.
Например:
src/
│
├── pages/
│ ├── Home/
│ ├── Login/
│ ├── Dashboard/
│ └── Article/
│
├── layouts/
│ ├── MainLayout.jsx
│ └── DashboardLayout.jsx
│
├── routes/
│ └── index.jsx
│
├── components/
│ └── ProtectedRoute.jsx
│
└── App.jsxВыносим маршруты в отдельный файл
Создадим:
routes/index.jsxНапример:
import {
Routes,
Route
} from "react-router-dom";
import Home from "../pages/Home";
import Dashboard from "../pages/Dashboard";
import ProtectedRoute from "../components/ProtectedRoute";
export default function AppRoutes() {
return (
<Routes>
<Route
path="/"
element={<Home />}
/>
<Route
element={<ProtectedRoute />}
>
<Route
path="/dashboard"
element={<Dashboard />}
/>
</Route>
</Routes>
);
}Теперь App.jsx становится чистым:
import {
BrowserRouter
} from "react-router-dom";
import AppRoutes from "./routes";
function App() {
return (
<BrowserRouter>
<AppRoutes />
</BrowserRouter>
);
}Это уже ближе к структуре настоящего проекта.
React Router и TypeScript
В современных React-проектах TypeScript используется всё чаще.
Особенно полезна типизация параметров маршрутов.
Например:
<Route
path="/users/:userId"
element={<User />}
/>В компоненте:
import {
useParams
} from "react-router-dom";
interface Params {
userId: string;
}
const {
userId
} = useParams<Params>();Теперь TypeScript понимает, какие параметры существуют.
Это особенно важно в больших приложениях, где десятки маршрутов и сотни компонентов.
Без типов легко получить ошибку:
undefined.userIdуже во время работы приложения.
React Router и Redux
Маршрутизация часто связана с состоянием пользователя.
Например:
авторизация;
права доступа;
настройки интерфейса.
Вместо:
localStorage.getItem("token")часто используют глобальное состояние.
Пример:
const user =
useSelector(
state => state.auth.user
);Теперь ProtectedRoute работает через Redux:
function ProtectedRoute() {
const user =
useSelector(
state => state.auth.user
);
return user
? <Outlet />
: <Navigate to="/login" />;
}На этом этапе у нас уже есть архитектура, которая подходит не только для учебного проекта, но и для реального React-приложения.
React Router DOM v7: что изменилось и стоит ли переходить
React Router постоянно развивается вместе с экосистемой React. Версия 7 стала логичным продолжением ветки 6.x: разработчики сохранили привычный подход с Routes, Route, Outlet и хуками, но сделали библиотеку более подготовленной для современных приложений.
Главное изменение - React Router постепенно выходит за рамки простого клиентского роутера. Теперь он лучше подходит для приложений, где нужно работать не только с отображением компонентов, но и с загрузкой данных, серверным рендерингом и более сложной архитектурой.
При этом для большинства обычных SPA-приложений переход не требует полного переписывания проекта.
Если ваш проект уже использует:
Routes
Route
Outlet
Link
useNavigate
useParamsто основная логика останется знакомой.
Нужно ли переходить с React Router v6 на v7
Однозначного ответа для всех проектов нет.
Если приложение небольшое и стабильно работает на v6, срочной необходимости менять версию нет.
Но для новых проектов в 2026 году имеет смысл сразу использовать актуальную версию, потому что она учитывает современные подходы React.
Особенно это актуально для:
новых SaaS-приложений;
сложных кабинетов;
проектов с большим количеством маршрутов;
приложений, где важна масштабируемая архитектура.
Главное правило: обновлять библиотеку стоит не ради самой версии, а ради решения конкретных задач.
React Router DOM vs Next.js App Router
Один из самых частых вопросов:
Что выбрать - React Router или Next.js?
Ответ зависит от типа проекта.
React Router DOM отлично подходит для приложений, где основная логика происходит на клиенте.
Например:
административные панели;
CRM;
внутренние сервисы;
личные кабинеты;
SaaS-продукты.
В таких проектах SEO часто не является главным приоритетом, поэтому SPA-подход полностью оправдан.
Next.js использует другой подход. Он сочетает серверный рендеринг, файловую маршрутизацию и возможности полноценного веб-фреймворка.
Для проектов, где важны:
поисковая оптимизация;
скорость первой загрузки;
контентные страницы;
интернет-магазины;
Next.js часто будет более подходящим вариантом.
Простая формула:
React Router DOM - приложение.
Next.js - полноценный веб-сайт или платформа.
React Router DOM vs TanStack Router
Ещё один современный конкурент - TanStack Router.
Его главное отличие - глубокая ориентация на TypeScript.
Например, он лучше подходит для проектов, где:
очень много динамических маршрутов;
сложные параметры URL;
требуется строгая типизация.
Однако React Router остаётся более распространённым решением благодаря огромной экосистеме и простоте входа.
Для большинства React-разработчиков React Router DOM всё ещё является стандартным выбором.
Типичные ошибки при работе с React Router
Даже опытные разработчики иногда сталкиваются с проблемами маршрутизации.
Разберём самые частые.
Ошибка: useNavigate работает только внутри Router
Сообщение:
useNavigate() may be used only in the context of a <Router>Причина:
компонент использует хук, но находится за пределами BrowserRouter.
Например:
function App() {
const navigate = useNavigate();
}без:
<BrowserRouter>
<App />
</BrowserRouter>React не понимает, откуда брать историю навигации.
Решение - проверить главный файл приложения.
Ошибка: страница показывает пустой экран при вложенном маршруте
Например:
URL:
/dashboard/settingsоткрывается, но компонент пустой.
Чаще всего причина:
в родительском Layout отсутствует:
<Outlet />React Router загрузил маршрут правильно, но ему некуда вставить дочерний компонент.
Ошибка: обновление страницы даёт 404
Ситуация:
переход внутри приложения работает:
site.ru/dashboardно после F5 сервер показывает ошибку.
Причина:
сервер пытается найти физический файл:
/dashboardа такого файла нет.
Для SPA сервер должен перенаправлять все неизвестные маршруты на:
index.htmlНапример, в Nginx обычно используется:
location / {
try_files $uri /index.html;
}После этого React Router сможет обработать URL самостоятельно.
Ошибка: слишком большой первоначальный JavaScript
Приложение работает, но первая загрузка медленная.
Причина:
весь код загружается сразу.
Решение:
использовать разделение кода:
const Settings = lazy(
() => import("./pages/Settings")
);Для больших проектов это один из самых простых способов улучшить скорость.
Как выглядит хорошая архитектура маршрутов в 2026
Для среднего React-приложения структура может выглядеть так:
src/
│
├── app/
│ └── router.jsx
│
├── pages/
│ ├── Home/
│ ├── Login/
│ ├── Dashboard/
│ └── Settings/
│
├── layouts/
│ ├── MainLayout.jsx
│ └── DashboardLayout.jsx
│
├── features/
│ └── auth/
│
├── components/
│ └── ProtectedRoute.jsx
│
└── main.jsxГлавная идея такой структуры:
маршруты не должны содержать бизнес-логику.
Они только отвечают за вопрос:
Какой компонент показать по этому адресу?
Мини-проект: структура реального приложения
Допустим, мы создаём платформу для публикации статей.
Маршруты:
/
├── Главная
/articles
├── Список статей
/articles/:slug
├── Страница статьи
/login
├── Авторизация
/dashboard
├── Кабинет автора
/dashboard/create
├── Создание статьи
/dashboard/settings
├── НастройкиАрхитектура будет выглядеть так:
<Routes>
<Route
path="/"
element={<MainLayout />}
>
<Route
index
element={<Home />}
/>
<Route
path="articles/:slug"
element={<Article />}
/>
</Route>
<Route
element={<ProtectedRoute />}
>
<Route
path="/dashboard"
element={<DashboardLayout />}
>
<Route
path="create"
element={<CreateArticle />}
/>
</Route>
</Route>
</Routes>Такой подход легко расширяется.
Можно добавить:
комментарии;
профили;
уведомления;
админку.
Не меняя основу маршрутизации.
FAQ: часто задаваемые вопросы
Что такое React Router DOM простыми словами?
React Router DOM - это библиотека, которая связывает URL браузера с React-компонентами. Она позволяет создавать многостраничное ощущение внутри одного SPA-приложения без полной перезагрузки.
Нужно ли устанавливать react-router отдельно?
Нет. Для браузерных приложений достаточно:
npm install react-router-domОн включает необходимые зависимости автоматически.
Когда использовать React Router, а когда Next.js?
React Router подходит для приложений, где основной упор идёт на клиентскую часть: кабинеты, SaaS, панели управления.
Next.js лучше подходит для сайтов, где важны SEO, серверный рендеринг и контентные страницы.
Как сделать защищённую страницу в React?
Используйте отдельный компонент ProtectedRoute, который проверяет авторизацию и через Navigate отправляет пользователя на страницу входа.
Зачем нужен Outlet?
Outlet показывает дочерние маршруты внутри родительского Layout.
Он позволяет создавать общие части интерфейса без копирования кода.
Нужно ли использовать lazy loading?
Для маленьких приложений это необязательно.
Но в больших проектах ленивую загрузку страниц стоит использовать, чтобы уменьшить размер первоначального JavaScript.
Чек-лист перед запуском React Router приложения
Перед публикацией проекта проверьте:
Маршруты имеют понятную структуру.
Приватные страницы закрыты через отдельную проверку доступа.
404-страница настроена через:
path="*"Вложенные страницы используют:
OutletБольшие разделы приложения загружаются через:
React.lazy()Сервер правильно настроен для SPA-маршрутов.
URL можно сохранить в закладки и открыть напрямую.
Заключение
React Router DOM остаётся одним из главных инструментов экосистемы React в 2026 году.
Но сегодня хороший роутер - это уже не просто переключение страниц.
Правильно построенная маршрутизация определяет архитектуру всего приложения: как организованы разделы, где находятся приватные зоны, как загружается код и насколько удобно проект расширять.
Начинать стоит с базовых вещей:
BrowserRouter, Routes, Route и Link.
Когда приложение начинает расти, добавляются:
Outlet, защищённые маршруты, lazy loading, TypeScript и отдельная архитектура маршрутов.
Именно такой подход позволяет превратить обычный React-проект в полноценное production-приложение.
Комментарии
Чтобы оставить комментарий, войдите в аккаунт.