Вложенные маршруты nested-routes - как построить удобную и масштабируемую навигацию

28 января 2026
Автор

Олег Марков

Введение

Вложенные маршруты (nested routes) помогают организовать навигацию в веб‑приложении так, чтобы она отражала реальную иерархию интерфейса и данных. Если вы создаете сложное SPA‑приложение с несколькими уровнями страниц, без вложенных маршрутов очень быстро появляется хаос: дублирование кода, сложное состояние и запутанные URL.

Смотрите, здесь важно понимать простую идею. Вложенный маршрут — это маршрут, который живет внутри другого маршрута и отображается в его «области». На уровне URL это выглядит как иерархия путей, например:

  • /dashboard
  • /dashboard/users
  • /dashboard/users/42

А на уровне интерфейса — как экран «Dashboard», внутри которого есть область для списка пользователей, а внутри нее — карточка конкретного пользователя.

В статье я покажу на примерах, как это устроено в типичном SPA на React с использованием React Router v6. Если вы работаете с Vue Router, Angular Router или другим похожим решением, общие принципы будут такими же: меняется синтаксис, но не сама концепция nested-routes.


Что такое вложенные маршруты и зачем они нужны

Логическая иерархия страниц

Когда у вас есть сущность верхнего уровня и вложенные сущности, естественно отразить это в маршрутах:

  • Раздел «Админка»
    • Список пользователей
      • Детальная страница пользователя
    • Настройки
    • Отчеты

Такую структуру удобно представить как дерево:

  • /admin
    • /admin/users
      • /admin/users/:id
    • /admin/settings
    • /admin/reports

Вложенные маршруты позволяют:

  • Разделить код на уровни — родительский маршрут отвечает за общий каркас страницы, а дочерние — за конкретное содержимое.
  • Переиспользовать общий layout — шапка, меню, подвал, боковая панель.
  • Управлять общим состоянием на уровне родителя — авторизация, загрузка данных, контекст.

Связь UI и структуры URL

Важно, что nested-routes поддерживают естественную связь:

  • Иерархия компонентов в интерфейсе.
  • Иерархия путей в URL.

Например, если вы отображаете компонент DashboardLayout, внутри него — навигацию, а под ней — область для контента, то дочерние маршруты будут как раз описывать, что отображается в этой области.

Это делает навигацию для пользователя предсказуемой, а код — более структурированным.


Базовая конфигурация вложенных маршрутов на примере React Router v6

Давайте разберемся на конкретном примере. Ниже — простейшее приложение с разделом /dashboard, внутри которого живут другие страницы.

Структура приложения

Представим такую структуру:

  • / — главная страница.
  • /dashboard — общий layout для кабинета.
  • /dashboard/profile — профиль.
  • /dashboard/settings — настройки.

Сейчас вы увидите, как это выглядит в коде.

// App.jsx
import { BrowserRouter, Routes, Route, Link } from "react-router-dom";

// Главная страница
function HomePage() {
  return (
    <div>
      {/* Ссылка в раздел дашборда */}
      <h1>Главная</h1>
      <Link to="/dashboard">Перейти в кабинет</Link>
    </div>
  );
}

// Layout для dashboard - родительский маршрут
import { Outlet } from "react-router-dom";

function DashboardLayout() {
  return (
    <div>
      {/* Общие части интерфейса дашборда */}
      <h1>Dashboard</h1>

      {/* Локальное меню для дочерних маршрутов */}
      <nav>
        <Link to="profile">Профиль</Link> |{" "}
        <Link to="settings">Настройки</Link>
      </nav>

      {/* Здесь будут рендериться дочерние маршруты */}
      <div style={{ marginTop: 20 }}>
        <Outlet />
      </div>
    </div>
  );
}

// Конкретные страницы внутри дашборда
function ProfilePage() {
  return <div>Страница профиля</div>;
}

function SettingsPage() {
  return <div>Страница настроек</div>;
}

export default function App() {
  return (
    // Корневой роутер
    <BrowserRouter>
      <Routes>
        {/* Обычный маршрут для главной страницы */}
        <Route path="/" element={<HomePage />} />

        {/* Родительский маршрут для dashboard */}
        <Route path="/dashboard" element={<DashboardLayout />}>
          {/* Вложенные маршруты с относительными путями */}
          <Route path="profile" element={<ProfilePage />} />
          <Route path="settings" element={<SettingsPage />} />
        </Route>
      </Routes>
    </BrowserRouter>
  );
}

Обратите внимание на несколько ключевых моментов:

  1. В родительском компоненте DashboardLayout мы используем Outlet.

    • Это «место», где будет рендериться содержимое дочернего маршрута.
    • Без Outlet вы не увидите дочерние страницы.
  2. Внутренние пути дочерних маршрутов profile и settings задаются без слеша в начале.

    • Это относительные пути внутри /dashboard.
    • В итоге формируются маршруты /dashboard/profile и /dashboard/settings.
  3. В навигации внутри DashboardLayout используем относительные ссылки to="profile" и to="settings".

    • Браузер (точнее, роутер) сам поймет, что нужно построить URL относительно /dashboard.

Относительные и абсолютные пути во вложенных маршрутах

Как формируется полный путь

В React Router (и в большинстве других роутеров) относительный путь дочернего маршрута просто дописывается к пути родителя:

  • Родитель: path /dashboard
  • Дочерний: path profile
  • Итоговый URL: /dashboard/profile

Если вы случайно укажете путь с ведущим слешем:

<Route path="/profile" element={<ProfilePage />} />

то маршрут станет корневым /profile, а не вложенным в /dashboard. Это распространенная ошибка.

Как правильно использовать относительные ссылки

Смотрите, я покажу вам два варианта ссылок:

// Относительная ссылка - предпочтительна внутри родителя
<Link to="profile">Профиль</Link>

// Абсолютная ссылка - тоже сработает, но привязана к корню
<Link to="/dashboard/profile">Профиль</Link>

Лучше использовать относительные ссылки, если вы находитесь внутри родительского маршрута. Это делает код более гибким: если вы вдруг измените путь родителя с /dashboard на /app, относительные ссылки продолжат работать без правок.


Индексные (index) маршруты внутри вложенных маршрутов

Часто нужно, чтобы при заходе на /dashboard без указания дополнительного сегмента отображалась «главная» страница этого раздела. Например, краткая статистика или приветственный экран.

Для этого используются индексные маршруты.

// Добавим индексный маршрут в наш пример
function DashboardHome() {
  return <div>Добро пожаловать в дашборд</div>;
}

export default function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<HomePage />} />

        <Route path="/dashboard" element={<DashboardLayout />}>
          {/* Index маршрут - сработает для /dashboard */}
          <Route index element={<DashboardHome />} />

          {/* Остальные дочерние маршруты */}
          <Route path="profile" element={<ProfilePage />} />
          <Route path="settings" element={<SettingsPage />} />
        </Route>
      </Routes>
    </BrowserRouter>
  );
}

Комментарии по коду:

  • index означает «маршрут по умолчанию» для данного родителя.
  • Такой маршрут не принимает path.
  • При переходе на /dashboard будет показан именно DashboardHome.

Это удобнее, чем создавать лишний путь path="" или path="/" и пытаться комбинировать его с родителем.


Вложенные маршруты нескольких уровней

Теперь давайте посмотрим, что будет, если у нас несколько уровней вложенности. Например:

  • /projects — список проектов.
  • /projects/:projectId — детальная страница проекта.
  • /projects/:projectId/tasks — задачи проекта.
  • /projects/:projectId/tasks/:taskId — конкретная задача.

Структура компонентов

Опишем уровни:

  • ProjectsLayout — общий layout для /projects.
  • ProjectsListPage — список всех проектов.
  • ProjectLayout — layout для конкретного проекта (шапка, меню).
  • ProjectOverviewPage — обзор проекта.
  • ProjectTasksPage — список задач проекта.
  • TaskDetailsPage — отдельная задача.

Теперь вы увидите, как это можно сконфигурировать.

// ProjectsLayout.jsx
import { Outlet, Link } from "react-router-dom";

export function ProjectsLayout() {
  return (
    <div>
      {/* Общий заголовок раздела Projects */}
      <h1>Проекты</h1>
      <nav>
        <Link to="">Список проектов</Link>
      </nav>

      {/* Вложенные маршруты /projects/... */}
      <Outlet />
    </div>
  );
}

// ProjectLayout.jsx
import { Outlet, useParams, NavLink } from "react-router-dom";

export function ProjectLayout() {
  // Получаем параметр projectId из URL
  const { projectId } = useParams();

  return (
    <div style={{ border: "1px solid #ccc", marginTop: 20, padding: 10 }}>
      {/* Шапка конкретного проекта */}
      <h2>Проект {projectId}</h2>

      {/* Локальное меню конкретного проекта */}
      <nav>
        <NavLink to="" end>
          Обзор
        </NavLink>{" "}
        |{" "}
        <NavLink to="tasks">
          Задачи
        </NavLink>
      </nav>

      {/* Вложенные маршруты /projects/:projectId/... */}
      <Outlet />
    </div>
  );
}

Теперь опишем сами маршруты:

// App.jsx (фрагмент с маршрутами проектов)
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { ProjectsLayout } from "./ProjectsLayout";
import { ProjectLayout } from "./ProjectLayout";

function ProjectsListPage() {
  return <div>Здесь список всех проектов</div>;
}

function ProjectOverviewPage() {
  return <div>Обзор выбранного проекта</div>;
}

function ProjectTasksPage() {
  return <div>Список задач проекта</div>;
}

function TaskDetailsPage() {
  return <div>Детали конкретной задачи</div>;
}

export default function App() {
  return (
    <BrowserRouter>
      <Routes>
        {/* ... другие маршруты */}

        <Route path="/projects" element={<ProjectsLayout />}>
          {/* /projects */}
          <Route index element={<ProjectsListPage />} />

          {/* /projects/:projectId */}
          <Route path=":projectId" element={<ProjectLayout />}>
            {/* /projects/:projectId */}
            <Route index element={<ProjectOverviewPage />} />

            {/* /projects/:projectId/tasks */}
            <Route path="tasks" element={<ProjectTasksPage />} />

            {/* /projects/:projectId/tasks/:taskId */}
            <Route path="tasks/:taskId" element={<TaskDetailsPage />} />
          </Route>
        </Route>
      </Routes>
    </BrowserRouter>
  );
}

Здесь используется несколько важных приемов:

  • Параметр маршрута :projectId объявлен на уровне Route path=":projectId".
  • Внутри ProjectLayout мы читаем его через useParams().
  • Дочерние маршруты tasks и tasks/:taskId автоматически включают projectId в итоговый путь.
  • Вы можете продолжать вкладывать маршруты дальше, если это действительно нужно.

Главная идея: каждый уровень маршрута отвечает за свой участок UI и свои данные. Так проще управлять сложным интерфейсом.


Работа с параметрами во вложенных маршрутах

Как читать параметры родителя и ребенка

Если путь такой: /projects/:projectId/tasks/:taskId, то внутри вложенных компонентов вы можете получить оба параметра:

import { useParams } from "react-router-dom";

function TaskDetailsPage() {
  const { projectId, taskId } = useParams();

  // Здесь вы можете загрузить данные задачи по projectId и taskId
  // и отрендерить их
  return (
    <div>
      <h3>Задача {taskId}</h3>
      <p>Принадлежит проекту {projectId}</p>
    </div>
  );
}

Комментарии:

  • useParams возвращает объект со всеми параметрами текущего пути и путей выше.
  • Если вы находитесь в дочернем маршруте, вы все равно видите параметры, объявленные выше в дереве маршрутов.

Когда параметры лучше поднимать на уровень родителя

Иногда вы загружаете данные проекта в ProjectLayout, а затем дочерние страницы используют эти данные. Это хороший подход:

  • Вы не дублируете запросы на каждую дочернюю страницу.
  • Передаете данные через контекст или пропсы.

Пример (упрощенный):

// ProjectLayout.jsx
import { Outlet, useParams } from "react-router-dom";
import { useEffect, useState } from "react";

export function ProjectLayout() {
  const { projectId } = useParams();
  const [project, setProject] = useState(null);

  useEffect(() => {
    // Здесь имитируем загрузку данных проекта
    // В реальном приложении будет запрос к API
    setProject({ id: projectId, name: "Demo project" });
  }, [projectId]);

  if (!project) {
    return <div>Загрузка проекта...</div>;
  }

  return (
    <ProjectContext.Provider value={project}>
      <h2>{project.name}</h2>
      {/* Далее обычный layout + Outlet */}
      <Outlet />
    </ProjectContext.Provider>
  );
}

Так вы используете вложенные маршруты не только для структуры URL, но и для организации логики данных.


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

Зачем выносить layout на уровень маршрута

Смотрите, я покажу вам типичную проблему. Если не использовать вложенные маршруты, вы можете начать дублировать layout в каждом компоненте страницы:

function ProfilePage() {
  return (
    <DashboardLayout>
      {/* Конкретное содержимое */}
      <div>Профиль</div>
    </DashboardLayout>
  );
}

function SettingsPage() {
  return (
    <DashboardLayout>
      <div>Настройки</div>
    </DashboardLayout>
  );
}

Проблемы:

  • Layout жестко «запаян» в каждую страницу.
  • Для смены layout нужно править много файлов.
  • Сложно использовать общий стейт и навигацию.

Вложенные маршруты решают это иначе:

// Один общий layout как маршрут
<Route path="/dashboard" element={<DashboardLayout />}>
  <Route index element={<DashboardHome />} />
  <Route path="profile" element={<ProfilePage />} />
  <Route path="settings" element={<SettingsPage />} />
</Route>

Здесь:

  • DashboardLayout рендерится всегда, когда URL начинается с /dashboard.
  • Конкретное содержимое внутри заменяется в Outlet.

Общие nested-layout уровни

Вы можете выстраивать целую цепочку layout-компонентов:

  • Общий layout приложения: AppLayout (шапка, подвал).
  • Layout для авторизованных пользователей: AuthLayout.
  • Layout для админки: AdminLayout.

Структура маршрутов будет выглядеть примерно так:

<Route path="/" element={<AppLayout />}>
  {/* Публичные страницы */}
  <Route index element={<LandingPage />} />
  <Route path="login" element={<LoginPage />} />

  {/* Защищенный блок */}
  <Route element={<AuthLayout />}>
    <Route path="profile" element={<ProfilePage />} />

    {/* Отдельный layout для админки */}
    <Route path="admin" element={<AdminLayout />}>
      <Route index element={<AdminHomePage />} />
      <Route path="users" element={<AdminUsersPage />} />
      <Route path="settings" element={<AdminSettingsPage />} />
    </Route>
  </Route>
</Route>

Обратите внимание:

  • Внутри AuthLayout мы можем проверять авторизацию и, если пользователь не авторизован, перенаправлять его на /login.
  • AuthLayout сам по себе не имеет path, он просто оборачивает вложенные маршруты.
  • Это еще один способ воспользоваться nested-routes для построения архитектуры приложения.

Защищенные разделы и nested-routes

Простой ProtectedRoute без вложенности

Обычно защищенный маршрут делают так:

import { Navigate } from "react-router-dom";

function ProtectedRoute({ isAuth, children }) {
  // Если пользователь не авторизован - отправляем на логин
  if (!isAuth) {
    return <Navigate to="/login" replace />;
  }

  // Иначе рендерим дочерний компонент
  return children;
}

Использование:

<Route
  path="/profile"
  element={
    <ProtectedRoute isAuth={isAuth}>
      <ProfilePage />
    </ProtectedRoute>
  }
/>

ProtectedRoute c nested-routes

Теперь посмотрим, как вложенные маршруты делают защиту более гибкой. Например, весь /dashboard должен быть доступен только после авторизации.

// ProtectedLayout.jsx
import { Navigate, Outlet } from "react-router-dom";

export function ProtectedLayout({ isAuth }) {
  if (!isAuth) {
    // Перенаправляем на страницу логина
    return <Navigate to="/login" replace />;
  }

  // Пользователь авторизован - рендерим вложенные маршруты
  return <Outlet />;
}

Использование в дереве маршрутов:

<Route element={<ProtectedLayout isAuth={isAuth} />}>
  <Route path="/dashboard" element={<DashboardLayout />}>
    <Route index element={<DashboardHome />} />
    <Route path="profile" element={<ProfilePage />} />
    <Route path="settings" element={<SettingsPage />} />
  </Route>
</Route>

Здесь ProtectedLayout — это «прослойка» над dashboard:

  • Он не имеет собственного path.
  • Он проверяет доступ и рендерит Outlet.
  • Все маршруты внутри автоматически становятся защищенными.

Это удобнее, чем добавлять проверку к каждому отдельному маршруту.


Обработка 404 и несовпадающих вложенных маршрутов

Ловим 404 на одном уровне вложенности

Внутри любого Route вы можете добавить маршрут со звездочкой:

<Route path="/dashboard" element={<DashboardLayout />}>
  <Route index element={<DashboardHome />} />
  <Route path="profile" element={<ProfilePage />} />
  <Route path="settings" element={<SettingsPage />} />

  {/* Локальный 404 для /dashboard/... */}
  <Route path="*" element={<div>Страница дашборда не найдена</div>} />
</Route>

Тогда:

  • /dashboard/unknown попадет в этот маршрут.
  • /unknown все равно будет искать 404 на глобальном уровне.

Глобальный 404

Глобальный 404 обычно располагают в конце списка корневых маршрутов:

<Routes>
  <Route path="/" element={<HomePage />} />
  <Route path="/dashboard" element={<DashboardLayout />}>
    {/* ... вложенные ... */}
  </Route>

  {/* Глобальный 404 */}
  <Route path="*" element={<div>Страница не найдена</div>} />
</Routes>

Важно следить за тем, чтобы звездочка не перехватывала то, что вы планировали обрабатывать на вложенном уровне.


Динамическая подгрузка страниц и код-сплиттинг во вложенных маршрутах

В приложениях с большим количеством nested-routes полезно разбивать код на чанки и подгружать их по мере необходимости.

Пример с React.lazy и Suspense

Давайте посмотрим, как это делается на практике:

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

// Динамическая загрузка страниц
const DashboardLayout = lazy(() => import("./DashboardLayout"));
const DashboardHome = lazy(() => import("./DashboardHome"));
const ProfilePage = lazy(() => import("./ProfilePage"));
const SettingsPage = lazy(() => import("./SettingsPage"));

export default function App() {
  return (
    <BrowserRouter>
      {/* Suspense оборачивает маршруты, чтобы показывать fallback при загрузке */}
      <Suspense fallback={<div>Загрузка...</div>}>
        <Routes>
          <Route path="/" element={<HomePage />} />

          {/* Layout и дочерние страницы будут подгружены по запросу */}
          <Route path="/dashboard" element={<DashboardLayout />}>
            <Route index element={<DashboardHome />} />
            <Route path="profile" element={<ProfilePage />} />
            <Route path="settings" element={<SettingsPage />} />
          </Route>
        </Routes>
      </Suspense>
    </BrowserRouter>
  );
}

Комментарии:

  • Здесь мы не загружаем код для dashboard до тех пор, пока пользователь не зайдет на /dashboard или один из вложенных маршрутов.
  • Nested-routes при этом продолжают работать как обычно, просто код компонентов подгружается лениво.

Типичные ошибки и как их избежать

Ошибка 1. Отсутствие Outlet в родительском компоненте

Симптом: вы настроили вложенные маршруты, но при переходе на них отображается только layout, а дочерняя страница не видна.

Причина: в родительском компоненте (DashboardLayout, ProjectLayout и т. д.) забыли добавить Outlet.

Решение:

import { Outlet } from "react-router-dom";

function DashboardLayout() {
  return (
    <div>
      {/* ... общий layout ... */}
      <Outlet /> {/* Обязательно */}
    </div>
  );
}

Ошибка 2. Использование абсолютного пути вместо относительного

Симптом: дочерний маршрут не является частью родительского и открывается по корневому пути.

Причина: указали path="/profile" вместо path="profile".

Решение: убедитесь, что у вложенных маршрутов нет ведущего слеша.

<Route path="/dashboard" element={<DashboardLayout />}>
  {/* Правильно */}
  <Route path="profile" element={<ProfilePage />} />

  {/* Неправильно - это уже корень */}
  {/* <Route path="/profile" element={<ProfilePage />} /> */}
</Route>

Ошибка 3. Смешивание index маршрутов и пустого path

Симптом: нестабильное поведение маршрутов по умолчанию внутри родителя.

Причина: пытаются использовать path="" вместо index.

Решение: для маршрута по умолчанию всегда используйте index.

<Route path="/dashboard" element={<DashboardLayout />}>
  <Route index element={<DashboardHome />} /> {/* Правильно */}
</Route>

Ошибка 4. Слишком глубокая вложенность без необходимости

Симптом: многоуровневое дерево маршрутов, в котором сложно ориентироваться, хотя UI не так уж глубок.

Причина: каждая мелкая часть интерфейса вынесена в отдельный route.

Решение:

  • Делайте nested-routes по уровню логики и по уровню URL.
  • Мелкие UI-компоненты лучше оставлять как обычные компоненты, а не отдельные маршруты.

Концептуальные преимущества nested-routes

Чтобы зафиксировать в голове общую картину, давайте кратко перечислим, что вы выигрываете, применяя вложенные маршруты:

  1. Структурированный URL

    • Отражает иерархию сущностей в приложении.
    • Позволяет пользователям ориентироваться в навигации.
  2. Переиспользуемые layout-компоненты

    • Один layout на группу страниц.
    • Легко менять общие части интерфейса.
  3. Локализованное состояние

    • Данные, загруженные в родителе, переиспользуются дочерними маршрутами.
    • Не нужно тянуть все в глобальный стейт без необходимости.
  4. Чистый код маршрутизации

    • Дерево маршрутов выглядит как дерево страниц.
    • Меньше дублирования и условий.
  5. Гибкая работа с правами доступа

    • Один защищающий layout над группой маршрутов.
    • Разделение логики авторизации по зонам приложения.

Заключение

Вложенные маршруты — это не просто удобная возможность роутера, а способ спроектировать архитектуру SPA‑приложения так, чтобы навигация, UI и данные были связаны в понятную иерархию.

Если суммировать основные практические моменты:

  • Родительский маршрут определяет общий layout и область для дочерних маршрутов через Outlet.
  • Дочерние маршруты задаются относительными путями и образуют читаемую структуру URL.
  • Параметры, объявленные выше, доступны во всех вложенных маршрутах, а данные, загруженные на уровне layout, легко передавать дальше.
  • Index маршруты помогают настроить поведение по умолчанию для разделов.
  • Nested-routes хорошо сочетаются с защищенными layout-компонентами и динамической подгрузкой кода.

Далее вы можете углубляться в продвинутые сценарии: раздельные роутеры для микрофронтендов, синхронизация вложенных маршрутов с состоянием вкладок, вложенные маршруты в модальных окнах и другие паттерны. Но базовые принципы всегда остаются теми же — каждый уровень маршрута отвечает за свой уровень интерфейса и логики.


Частозадаваемые технические вопросы по теме статьи и ответы на них

Как во вложенных маршрутах открыть модальное окно по URL а не обычную страницу

Можно сделать отдельный маршрут под модальное состояние и рендерить его поверх предыдущей страницы. Общая идея такая:

  1. При навигации на маршрут с модальным окном сохраняйте «фон» в состоянии роутера state: { background: location }.
  2. В корневом компоненте смотрите location.state?.background и:
    • рендерите основной Routes по background
    • поверх него рендерите отдельный Routes только с модальными маршрутами.

Так модальное окно будет иметь свой URL, но базовая страница останется видимой под ним.

Как правильно использовать nested-routes с HashRouter

С точки зрения nested-routes разница только в том, что перед путями в адресной строке стоит #. Структура Routes, Route, Outlet и относительные пути остаются такими же. Важно:

  • Не использовать абсолютные ссылки с полным URL, если вы планируете перенос между BrowserRouter и HashRouter.
  • Стараться везде использовать относительные ссылки, чтобы не зависеть от типа роутера.

Как сделать так чтобы часть вложенных маршрутов кешировалась а часть нет

Один из практичных подходов — оборачивать нужные дочерние маршруты в компонент, который сам управляет кешированием. Например:

  1. Создать KeepAliveLayout, который:
    • хранит отрендеренный дочерний компонент в useRef или контексте;
    • при повторном заходе возвращает закешированный результат.
  2. В маршрутах обернуть нужную группу:
    • Route element={<KeepAliveLayout />}><Route path="..." ... />.

Альтернатива — библиотечные решения для React (например, компоненты для кеширования маршрутов), которые применяются аналогично.

Как реализовать разный layout для одной и той же страницы в зависимости от пути

Можно объявить один и тот же компонент как element в разных маршрутах с разными родительскими layout:

  • /dashboard/reports → родитель DashboardLayout.
  • /admin/reports → родитель AdminLayout.

В обоих случаях дочерний маршрут может использовать один компонент ReportsPage. Логика страницы остается одной, но окружение (layout) меняется в зависимости от дерева маршрутов.

Как во вложенных маршрутах менять заголовок страницы и хлебные крошки

Обычно делают так:

  1. На каждом уровне layout добавляют метаданные маршрута (например, через собственную конфигурацию или контекст).
  2. Родительский layout читает эти метаданные и:
    • формирует цепочку хлебных крошек на основе текущего пути и вложенных маршрутов;
    • устанавливает document.title в useEffect.

Если вы используете конфигурационный подход (массив объектов с описанием маршрутов), можно хранить заголовок прямо в конфиге и строить breadcrumbs, проходясь по дереву маршрутов и текущему location.pathname.

Стрелочка влевоПрограммная навигация - что это такое и как с ней работать в современных приложенияхНавигационные хуки navigation guards - полный практический разборСтрелочка вправо

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

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

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

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

Все гайды по Vue

Руководство по валидации форм во Vue.jsИнтеграция Tiptap для создания редакторов на VueРабота с таблицами во Vue через TanStackИнструкция по установке и компонентам Vue sliderУправление пакетами Vue js с помощью npmУправление пакетами и node modules в Vue проектахКак использовать meta для улучшения SEO на VueПолный гайд по компоненту messages во Vuejs5 правил использования Inertia с Vue и LaravelРабота с модулями и пакетами в VueИнструкция по работе с grid на VueGithub для Vue проектов - подробная инструкция по хранению и совместной работеНастройка ESLint для Vue проектов и поддержка качества кодаОбработка ошибок и отладка в Vue.jsИспользование Vue Devtools для отладки и мониторинга приложенийРабота с конфигурационными файлами и скриптами VueСоздание и настройка проектов Vue с помощью Vue CLI3 способа интеграции Chart.js с Vue для создания графиковРабота с Canvas во VueИнструкция по реализации календаря во VueРабота с Ant Design Vue для создания UI на Vue
Vuex - полное руководство по управлению состоянием во Vue приложенияхРеактивные ссылки ref - полный разбор для разработчиковРеактивные объекты reactive-objects - подробное руководство с примерамиРеактивные переменные - концепция reactive и практические примерыМеханизм Provide Inject - как он работает и когда применятьPinia современный менеджер состояния для VueЛокальное состояние local state в веб разработкеГлобальное состояние в приложениях - global state
Обзор и использование утилит Vue для удобной разработкиРабота с обновлениями компонента и жизненным циклом updateРазрешение конфликтов и ошибок с помощью Vue resolveИспользование query-параметров и их обработка в маршрутах VueЗагрузка и управление состоянием загрузки в VueИспользование библиотек Vue для расширения функционалаРабота с JSON данными в приложениях VueКак работать с экземплярами компонента Instance во VueПолучение данных и API-запросы во Vue.jsЭкспорт и импорт данных и компонентов в VueОбработка событий и их передача между компонентами VuejsГайд по defineEmits на Vue 3Понимание core функционала Vue и его применениеПонимание и применение Composition API в Vue 3Понимание и работа с компилятором VueКогда и как использовать $emit и call во VueВзаимодействие с внешними API через Axios в Vue
Веб приложения на Vue архитектура и лучшие практикиИспользование Vite для быстрого старта и сборки проектов на Vue 3Работа с URL и ссылками в приложениях на VueРабота с пользовательскими интерфейсами и UI библиотеками во VueОрганизация и структура исходных файлов в проектах VueИспользование Quasar Framework для разработки на Vue с готовыми UI-компонентамиОбзор популярных шаблонов и стартовых проектов на VueИнтеграция Vue с PHP для создания динамичных веб-приложенийКак организовать страницы и маршруты в проекте на VueNuxt JS и Vue 3 для SSR приложенийСоздание серверных приложений на Vue с помощью Nuxt jsИспользование Vue Native для разработки мобильных приложенийОрганизация и управление индексной страницей в проектах VueИспользование Docker для контейнеризации приложений на VueИнтеграция Vue.js с Django для создания полноценных веб-приложенийСоздание и работа с дистрибутивом build dist Vue приложенийРабота со стилями и CSS в Vue js для красивых интерфейсовСоздание и структурирование Vue.js приложенияКак исправить ошибку cannot find module vueНастройка и сборка проектов Vue с использованием современных инструментовИнтеграция Vue с Bitrix для корпоративных решенийРазработка административных панелей на Vue js
Функция append в Go GolangОтображение компонента mounted - практическое руководствоХуки жизненного цикла компонентов - полное руководство для разработчиковУничтожение компонента destroyed - как правильно очищать ресурсы и подпискиИнициализация данных в состоянии created - как и когда подготавливать данные в приложенииОбновление компонента beforeUpdate во VueМонтирование компонента - хук beforeMount в VueРазрушение компонента во Vue - beforeDestroy и beforeUnmountСоздание экземпляра beforeCreate - полный разбор жизненного цикла
5 библиотек для создания tree view во VueИнтеграция Tailwind CSS с Vue для современных интерфейсовИнтеграция Vue с серверной частью и HTTPS настройкамиКак обрабатывать async операции с Promise во VueИнтеграция Node.js и Vue.js для разработки приложенийРуководство по интеграции Vue js в NET проектыПримеры использования JSX во VueГайд по импорту и регистрации компонентов на VueМногоязычные приложения на Vue с i18nИнтеграция FLIR данных с Vue5 примеров использования filter во Vue для упрощения разработки3 примера реализации drag-and-drop во Vue
Слоты компонента - концепция и практическое использованиеРегистрация компонентов component-registration в приложениях с внедрением зависимостейProps компонента в React - полный разбор с примерамиФункциональные компоненты в React - функциональный подход к построению интерфейсовСобытия компонента - events в современных интерфейсахДинамические компоненты - dynamic-componentsСоздание компонента component - практическое руководствоАсинхронные компоненты async-components - практическое руководство
Наблюдатели watchers - от паттерна до практических реализацийУправление переменными и реактивными свойствами во VueИспользование v for и slot в VueПрименение v-bind для динамической привязки атрибутов в VueУправление пользователями и их данными в Vue приложенияхСоздание и использование UI Kit для Vue приложенийТипизация и использование TypeScript в VuejsШаблоны Vue templates - практическое руководство для разработчиковИспользование шаблонов в Vue js для построения интерфейсовИспользование Swiper для создания слайдеров в VueРабота со стилями и стилизацией в VueСтруктура и особенности Single File Components SFC в VueРабота со SCSS в проектах на Vue для стилизацииРабота со скроллингом и прокруткой в Vue приложенияхПрименение script setup синтаксиса в Vue 3 для упрощения компонентовИспользование scoped стилей для изоляции CSS в компонентах Vue3 способа улучшить навигацию Vue с push()Обработка запросов и асинхронных операций в VueРеактивность Vue reactivity - как это работает под капотом и как этим пользоватьсяПонимание и использование provide inject для передачи данных между компонентамиПередача и использование props в Vue 3 для взаимодействия компонентовПередача данных между компонентами с помощью props в Vue jsУправление property и функциями во Vue.jsРабота со свойствами компонентов VueУправление параметрами и динамическими данными во VueОпции компонента в Go - паттерн component-optionsРабота с lifecycle-хуком onMounted во VueОсновы работы с объектами в VueПонимание жизненного цикла компонента Vue js на примере mountedИспользование модальных окон modal в Vue приложенияхИспользование методов в компонентах Vue для обработки логикиИспользование метода map в Vue для обработки массивовИспользование хуков жизненного цикла Vue для управления состоянием компонентаРабота с ключами key в списках и компонентах VueОбработка пользовательского ввода в Vue.jsРабота с изображениями и их оптимизация в VueИспользование хуков жизненного цикла в VueОрганизация сеток и гридов для верстки интерфейсов на VueСоздание и управление формами в VueОрганизация файлов и структура проекта Vue.jsКомпоненты Vue создание передача данных события и emitРабота с динамическими компонентами и данными в Vue3 способа манипулирования DOM на VueРуководство по div во VueИспользование директив в Vue и их расширенные возможностиОсновы и применение директив в VueИспользование директив и их особенности на Vue с помощью defineИспользование компонентов datepicker в Vue для выбора датОрганизация циклов и итераций во VueКак работает компиляция Vue CoreВычисляемые свойства computed во Vue.jsСоздание и использование компонентов в Vue JSОбработка кликов и пользовательских событий в VueИспользование классов в Vue для организации кода и компонентовИспользование директивы checked для управления состоянием чекбоксов в VueГайд на checkbox компонент во VueОтображение данных в виде графиков с помощью Vue ChartСоздание и настройка кнопок в VueСоздание и настройка кнопок в Vue приложенияхРабота с lifecycle-хуками beforeCreate и beforeMount во VueОсновы Vue - vue-basics для уверенного стартаИспользование массивов и методов их обработки в VueИспользование массивов и их обработка в Vue
Использование Vuetify для создания современных интерфейсов на VueИспользование transition во VueТестирование компонентов и приложений на VueТелепортация - архитектура и реализация в серверных приложенияхРабота с teleport для управления DOM во VueSuspense в React - управление асинхронными данными и ленивой загрузкойПять шагов по настройке SSR в VuejsИспользование Shadcn UI компонентов с Vue для продвинутых интерфейсовИспользование router-link для навигации в Vue RouterКак использовать require в Vue для динамического импорта модулейРабота с динамическим рендерингом и виртуальным DOM на Vue.jsИспользование ref для управления ссылками и реактивностью в Vue 3Использование Vue Pro и его преимущества для профессиональной разработкиПлагины Vue vue-plugins - полное практическое руководствоРуководство по nextTick для работы с DOMМиксины - mixins в современном программированииJSX в Vue с использованием плагина vue-jsxСоздание и использование компонентов с помощью Vue js и CУправление состоянием и реактивностью через inject и provideДинамическое обновление компонентов и данных на VueГлубокое изучение документации Vue и как эффективно её использоватьКастомные элементы - Custom Elements в современном JavaScriptИспользование Crystal с Vue для разработкиИспользование вычисляемых свойств для динамического отображения данных на Vue jsОптимизация производительности и предупреждения в Vue
Открыть базу знаний

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

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

Vue 3 и Pinia

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

TypeScript с нуля

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

Next.js - с нуля

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

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