React Router: Навігаційна система сучасного вебу

Вкладені Маршрути та Макети

Це "кілер-фіча" React Router. Якщо ви зрозумієте цей розділ, ви зрозумієте 80% архітектури складних React-додатків.

Вкладені Маршрути та Макети

Це "кілер-фіча" React Router. Якщо ви зрозумієте цей розділ, ви зрозумієте 80% архітектури складних React-додатків.

У більшості роутерів (в інших мовах чи фреймворках) маршрут — це просто спосіб зіставити URL з екраном. В React Router маршрут — це спосіб зіставити сегмент URL з сегментом UI.

1. Ментальна модель: UI вкладений в URL

Подивіться на типовий веб-інтерфейс, наприклад, панель керування:

/dashboard/settings/profile
+----------------------------------------------------+
|  Global Navbar (Лого, Меню)                        |
| +------------------------------------------------+ |
| |  Dashboard Sidebar (Меню дашборда)             | |
| | +--------------------------------------------+ | |
| | |  Settings Tabs (Профіль, Безпека)          | | |
| | | +----------------------------------------+ | | |
| | | |                                        | | | |
| | | |  Profile Form (Власне контент)         | | | |
| | | |                                        | | | |
| | | +----------------------------------------+ | | |
| | +--------------------------------------------+ | |
| +------------------------------------------------+ |
+----------------------------------------------------+
  • / відповідає за Global Navbar
  • /dashboard додає Sidebar
  • /settings додає Tabs
  • /profile показує Form

Коли URL змінюється з /dashboard/settings/profile на /dashboard/settings/security:

  1. Navbar залишається.
  2. Sidebar залишається.
  3. Tabs залишаються.
  4. Змінюється тільки форма всередині.

React Router дозволяє відобразити цю вкладеність прямо в конфігурації маршрутів.

2. Компонент <Outlet>

Щоб реалізувати цю вкладеність, батьківський маршрут повинен знати, куди саме вставити дочірній компонент. Для цього використовується компонент <Outlet>.

Думайте про <Outlet> як про props.children, але контрольований роутером.

3. Створення Root Layout (Кореневий Макет)

У попередніх уроках ми мали проблему: навігаційне меню довелося б копіювати на кожну сторінку. Давайте виправимо це, створивши спільний макет для всього сайту.

Крок 1: Створення компонента Layout

src/layouts/RootLayout.jsx
import { Outlet, NavLink } from 'react-router-dom'

export default function RootLayout() {
    return (
        <div className="app-container">
            {/* Ця частина буде на ВСІХ сторінках */}
            <header>
                <nav>
                    <NavLink to="/">Головна</NavLink>
                    <NavLink to="/about">Про нас</NavLink>
                </nav>
            </header>

            {/* Сюди роутер підставить контент поточної сторінки */}
            <main>
                <Outlet />
            </main>

            <footer>
                <p>© 2026 Мій Сайт</p>
            </footer>
        </div>
    )
}

Крок 2: Оновлення конфігурації роутера

Тепер ми використаємо властивість children в об'єкті маршруту.

src/main.jsx
import RootLayout from './layouts/RootLayout'
// ... інші імпорти

const router = createBrowserRouter([
    {
        path: '/',
        element: <RootLayout />, // Головний батько
        errorElement: <ErrorPage />,
        children: [
            {
                // Це Index Route (про нього нижче)
                index: true,
                element: <HomePage />,
            },
            {
                path: 'about', // Зверніть увагу: без слеша "/" на початку!
                element: <AboutPage />,
            },
        ],
    },
])

Що змінилося:

  1. Коли URL /, рендериться <RootLayout>. Всередині його <Outlet /> рендериться <HomePage>.
  2. Коли URL /about, рендериться <RootLayout>. Всередині його <Outlet /> рендериться <AboutPage>.

4. Index Routes (Індексні маршрути)

Ви могли помітити index: true замість path: "" або path: "/".

Індексний маршрут — це "маршрут за замовчуванням" для батьківського шляху. Коли URL точно збігається з шляхом батька (у нашому випадку /), батько показує element, але в <Outlet> було б порожньо, якби не індексний маршрут.

Уявіть це як index.html у папці.

children: [
    {
        index: true, // URL: /
        element: <Home />,
    },
    {
        path: 'products', // URL: /products
        element: <ProductsLayout />,
        children: [
            { index: true, element: <AllProductsList /> }, // URL: /products
            { path: 'new', element: <NewProductForm /> }, // URL: /products/new
        ],
    },
]

5. Відносні шляхи (Relative Paths)

Зверніть увагу, що у дочірніх маршрутах ми пишемо path: "about", а не path: "/about".

  • Абсолютний шлях (/about): Завжди рахується від кореня домену.
  • Відносний шлях (about): Рахується від шляху батька.

Якщо батько має шлях /dashboard, а дитина settings, то кінцевий URL буде /dashboard/settings. Це дозволяє легко змінювати URL батька, не ламаючи всі дочірні маршрути.

6. Приклад: Глибока вкладеність

Давайте реалізуємо структуру з початку уроку.

const router = createBrowserRouter([
    {
        path: '/',
        element: <RootLayout />, // Має <Outlet>
        children: [
            {
                path: 'dashboard',
                element: <DashboardLayout />, // Має свій <Outlet>!
                children: [
                    {
                        index: true,
                        element: <DashboardHome />, // Статистика
                    },
                    {
                        path: 'settings',
                        element: <SettingsLayout />, // І тут теж <Outlet>!
                        children: [
                            { index: true, element: <ProfileSettings /> },
                            { path: 'security', element: <SecuritySettings /> },
                        ],
                    },
                ],
            },
        ],
    },
])

Як це рендериться при URL /dashboard/settings/security:

<RootLayout>
    <DashboardLayout>
        <SettingsLayout>
            <SecuritySettings />
        </SettingsLayout>
    </DashboardLayout>
</RootLayout>

Кожен макет відповідає тільки за свою частину UI і делегує решту через <Outlet />. Це робить код неймовірно модульним.

Резюме

  • Вкладені маршрути дозволяють будувати UI, який складається з вкладених шарів.
  • <Outlet> — це місце, куди батьківський маршрут виводить дочірній.
  • Index Route — це те, що показується в <Outlet>, коли URL точно збігається з батьківським.
  • Відносні шляхи спрощують рефакторинг і читання конфігурації.

Ми побудували статичну структуру. Але що робити, якщо частина URL — це динамічний ID (наприклад, /product/123)? Про це — у наступному розділі.

Copyright © 2026