Вложенные маршруты
Вложенные маршруты (nested routes) — иерархическая структура URL, при которой дочерние маршруты рендерятся внутри родительского компонента через специальный placeholder (Outlet в react-router).
Зачем нужно
Современные приложения имеют устойчивые layout-элементы: боковое меню, шапку, breadcrumbs. Вложенные маршруты позволяют описать эту иерархию один раз: родительский маршрут рендерит layout, дочерние — меняющийся контент через Outlet. Это устраняет дублирование layout-кода и делает структуру URL значимой.
Где используется
- Дашборды:
/dashboard/analytics,/dashboard/users,/dashboard/settings - Профиль пользователя:
/profile/info,/profile/security,/profile/billing - Административные панели с боковым меню
- Любое приложение с persistent navigation и меняющимся контентом
Nested Routes в React Router v6
import { BrowserRouter, Routes, Route, Outlet, Link } from 'react-router-dom';
// Layout-компонент с Outlet для дочернего контента
function DashboardLayout() {
return (
<div className="dashboard">
<aside className="sidebar">
<nav>
<Link to="analytics">Аналитика</Link>
<Link to="users">Пользователи</Link>
<Link to="settings">Настройки</Link>
</nav>
</aside>
<main className="content">
{/* Здесь рендерится активный дочерний маршрут */}
<Outlet />
</main>
</div>
);
}
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
{/* Родительский маршрут — DashboardLayout */}
<Route path="dashboard" element={<DashboardLayout />}>
{/* index — маршрут по умолчанию (/dashboard) */}
<Route index element={<DashboardHome />} />
{/* Дочерние маршруты (/dashboard/analytics и т.д.) */}
<Route path="analytics" element={<Analytics />} />
<Route path="users" element={<UsersList />} />
<Route path="users/:userId" element={<UserDetail />} />
<Route path="settings" element={<Settings />} />
</Route>
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
);
}
Три уровня вложенности
function ProfileLayout() {
return (
<div>
<h1>Профиль</h1>
<nav>
<Link to="info">Информация</Link>
<Link to="security">Безопасность</Link>
</nav>
<Outlet />
</div>
);
}
function SecurityLayout() {
return (
<div>
<h2>Безопасность</h2>
<nav>
<Link to="password">Пароль</Link>
<Link to="2fa">Двухфакторная</Link>
</nav>
<Outlet />
</div>
);
}
// Роутер с тремя уровнями:
<Route path="profile" element={<ProfileLayout />}>
<Route index element={<ProfileInfo />} />
<Route path="security" element={<SecurityLayout />}>
<Route index element={<SecurityOverview />} />
<Route path="password" element={<ChangePassword />} />
<Route path="2fa" element={<TwoFactor />} />
</Route>
</Route>
// URL: /profile/security/password
// Рендер: ProfileLayout > SecurityLayout > ChangePassword
Доступ к родительским параметрам
// Родительский: /projects/:projectId
// Дочерний: /projects/:projectId/tasks/:taskId
function TaskDetail() {
// useParams даёт все параметры из пути, включая родительские
const { projectId, taskId } = useParams;
return (
<div>
Проект: {projectId}, Задача: {taskId}
</div>
);
}
Частые ошибки
- Забывают
<Outlet />— без него дочерние маршруты не рендерятся; компонент-родитель должен явно указать место для вставки. - Используют абсолютные пути в дочерних Link — внутри вложенного роутера
to="/dashboard/users"работает, ноto="users"(относительный) лучше: не зависит от родительского пути. - Дублируют layout — создают одинаковый header/sidebar в каждом компоненте вместо вынесения в родительский layout с Outlet.
Связанные темы
- _MOC SPA
- Клиентский роутинг -- как работает
- Динамические маршруты
- Route Guards -- защита маршрутов
- Lazy Loading маршрутов