Пошук уроків, статей та іншого контенту
Створите спільні Layout Routes із навігацією, хедером і футером для групи сторінок через Outlet.
Layout route — це маршрут-контейнер, який відображає спільну структуру для кількох сторінок:
хедер;
навігацію;
футер;
бічну панель;
спільну перевірку доступу або інший wrapper.
Вкладена сторінка відображається всередині layout через компонент Outlet.
Наприклад, кілька сторінок можуть мати однаковий хедер і футер:
AppLayout
├── HomePage
├── AboutPage
└── ContactsPageLayout не потрібно дублювати в кожному компоненті сторінки. Він описується один раз у дереві маршрутів.
OutletOutlet — це місце, у якому React Router відрендерить активний дочірній маршрут.
import { Outlet } from "react-router-dom";
function AppLayout() {
return (
<>
<header>Хедер</header>
<main>
<Outlet />
</main>
<footer>Футер</footer>
</>
);
}Якщо активний маршрут відповідає HomePage, то HomePage буде відображено замість Outlet:
<header />
<main>
<HomePage />
</main>
<footer />Layout route створюється за допомогою вкладених компонентів Route:
<Routes>
<Route element={<AppLayout />}>
<Route path="/" element={<HomePage />} />
<Route path="/about" element={<AboutPage />} />
</Route>
</Routes>У цьому прикладі AppLayout є батьківським маршрутом для двох сторінок.
Коли користувач переходить на /about:
React Router відображає AppLayout;
усередині його Outlet відображає AboutPage.
Для навігації між маршрутами використовується Link або NavLink.
NavLink зручний для меню, тому що він дозволяє визначити активний маршрут і додати йому окремий клас.
<NavLink
to="/about"
className={({ isActive }) => (isActive ? "active" : "")}
>
Про нас
</NavLink>Функція className отримує об'єкт із властивістю isActive. Вона дорівнює true, якщо посилання відповідає поточному маршруту.
Нижче наведено повний приклад застосунку зі спільним хедером, навігацією та футером.
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import {
BrowserRouter,
NavLink,
Outlet,
Route,
Routes,
} from "react-router-dom";
function AppLayout() {
return (
<div className="app">
<header className="header">
<h1>Мій застосунок</h1>
<nav aria-label="Основна навігація">
<NavLink
to="/"
end
className={({ isActive }) => (isActive ? "active" : "")}
>
Головна
</NavLink>
<NavLink
to="/about"
className={({ isActive }) => (isActive ? "active" : "")}
>
Про нас
</NavLink>
<NavLink
to="/contacts"
className={({ isActive }) => (isActive ? "active" : "")}
>
Контакти
</NavLink>
</nav>
</header>
<main className="content">
<Outlet />
</main>
<footer className="footer">
<p>© 2025 Мій застосунок</p>
</footer>
</div>
);
}
function HomePage() {
return (
<section>
<h2>Головна сторінка</h2>
<p>Вітаємо на головній сторінці.</p>
</section>
);
}
function AboutPage() {
return (
<section>
<h2>Про нас</h2>
<p>Ця сторінка використовує той самий layout.</p>
</section>
);
}
function ContactsPage() {
return (
<section>
<h2>Контакти</h2>
<p>Зв’яжіться з нами зручним для вас способом.</p>
</section>
);
}
function NotFoundPage() {
return (
<section>
<h2>Сторінку не знайдено</h2>
<p>Перевірте адресу сторінки.</p>
</section>
);
}
function App() {
return (
<BrowserRouter>
<Routes>
<Route element={<AppLayout />}>
<Route path="/" element={<HomePage />} />
<Route path="/about" element={<AboutPage />} />
<Route path="/contacts" element={<ContactsPage />} />
<Route path="*" element={<NotFoundPage />} />
</Route>
</Routes>
</BrowserRouter>
);
}
createRoot(document.getElementById("root")).render(
<StrictMode>
<App />
</StrictMode>,
);У цьому прикладі:
AppLayout містить спільний інтерфейс;
Outlet є місцем для поточної сторінки;
NavLink створює навігацію;
end на посиланні для / не дозволяє вважати головну сторінку активною під час переходу, наприклад, на /about;
маршрут * показує сторінку для невідомих адрес.
Layout може об'єднувати сторінки певної частини застосунку. Наприклад, сторінки профілю можуть мати спільну бічну панель.
import { NavLink, Outlet } from "react-router-dom";
function ProfileLayout() {
return (
<div className="profile-layout">
<aside>
<h2>Профіль</h2>
<nav>
<NavLink to="/profile" end>
Огляд
</NavLink>
<NavLink to="/profile/settings">
Налаштування
</NavLink>
</nav>
</aside>
<section>
<Outlet />
</section>
</div>
);
}Такий layout підключається як батьківський маршрут:
<Routes>
<Route element={<AppLayout />}>
<Route path="/" element={<HomePage />} />
<Route path="/profile" element={<ProfileLayout />}>
<Route index element={<ProfileOverviewPage />} />
<Route path="settings" element={<ProfileSettingsPage />} />
</Route>
</Route>
</Routes>Тут утворюється два рівні layout:
AppLayout
└── ProfileLayout
├── ProfileOverviewPage
└── ProfileSettingsPageДля адреси /profile/settings React Router відобразить:
AppLayout;
ProfileLayout у AppLayout через його Outlet;
ProfileSettingsPage у ProfileLayout через його Outlet.
Для дочірнього маршруту, який має відображатися за адресою батьківського маршруту, використовується index:
<Route path="/profile" element={<ProfileLayout />}>
<Route index element={<ProfileOverviewPage />} />
<Route path="settings" element={<ProfileSettingsPage />} />
</Route>У цьому прикладі:
/profile відображає ProfileOverviewPage;
/profile/settings відображає ProfileSettingsPage.
Індексний маршрут не має власного path, оскільки він відповідає точному адресу батьківського маршруту.
Шляхи дочірніх маршрутів зазвичай задаються без початкового /:
<Route path="/profile" element={<ProfileLayout />}>
<Route index element={<ProfileOverviewPage />} />
<Route path="settings" element={<ProfileSettingsPage />} />
</Route>path="settings" автоматично стає частиною батьківського шляху:
/profile/settingsУ навігації можна використовувати відносний шлях:
<NavLink to="settings">Налаштування</NavLink>Він буде розрахований від поточного маршруту /profile.
Також можна вказати повний шлях:
<NavLink to="/profile/settings">Налаштування</NavLink>Відносні шляхи зручні, коли layout і його дочірні маршрути переміщуються як одна група.
Layout routes можна вкладати один в один. Це корисно, коли різні частини інтерфейсу мають різні спільні елементи.
<Routes>
<Route element={<AppLayout />}>
<Route path="/" element={<HomePage />} />
<Route path="/admin" element={<AdminLayout />}>
<Route index element={<AdminDashboardPage />} />
<Route path="users" element={<AdminUsersPage />} />
</Route>
</Route>
</Routes>Для /admin/users структура відображення буде такою:
AppLayout
└── AdminLayout
└── AdminUsersPageКожен layout повинен мати власний Outlet:
function AdminLayout() {
return (
<>
<aside>Адмінське меню</aside>
<Outlet />
</>
);
}Якщо в AdminLayout не буде Outlet, дочірня сторінка не відобразиться.
У прикладах AppLayout не має path:
<Route element={<AppLayout />}>
{/* Дочірні маршрути */}
</Route>Такий маршрут називають layout route без адреси. Він потрібен для спільної структури, але не додає окремий сегмент до URL.
Натомість маршрут із path="/profile" одночасно:
додає /profile до адреси;
визначає спільний layout для вкладених сторінок.
<Route path="/profile" element={<ProfileLayout />}>
{/* /profile і /profile/settings */}
</Route>Outletfunction AppLayout() {
return (
<>
<header>Хедер</header>
<footer>Футер</footer>
</>
);
}У такому випадку дочірні сторінки не відображатимуться. Layout повинен містити:
<Outlet /><Routes>
<Route element={<AppLayout />} />
<Route path="/about" element={<AboutPage />} />
</Routes>AboutPage не буде дочірнім маршрутом AppLayout, тому layout не застосовується до цієї сторінки.
Правильний варіант:
<Routes>
<Route element={<AppLayout />}>
<Route path="/about" element={<AboutPage />} />
</Route>
</Routes>/ у дочірньому маршрутіКоли маршрут має бути вкладеним, використовуйте відносний шлях:
<Route path="/profile" element={<ProfileLayout />}>
<Route path="settings" element={<ProfileSettingsPage />} />
</Route>Так маршрут буде доступний за адресою /profile/settings.
end для посилання на головну сторінкуБез end посилання на / може залишатися активним і для інших маршрутів. Для точного збігу використовуйте:
<NavLink to="/" end>
Головна
</NavLink>Не потрібно додавати хедер і футер до кожної сторінки:
function AboutPage() {
return (
<>
<header>...</header>
<h1>Про нас</h1>
<footer>...</footer>
</>
);
}Краще винести спільну структуру в layout, а сторінці залишити тільки власний вміст:
function AboutPage() {
return <h1>Про нас</h1>;
}Layout route об'єднує кілька сторінок спільною структурою.
Outlet визначає місце для відображення активного дочірнього маршруту.
Хедер, футер і навігацію зручно розміщувати в layout.
Дочірні маршрути описують вкладеними компонентами Route.
Для меню з активним пунктом використовуйте NavLink.
index відповідає точному шляху батьківського маршруту.
Layout routes можна вкладати один в один.
Кожен layout, який має дочірні маршрути, повинен містити Outlet.