Пошук уроків, статей та іншого контенту
Організуєте вкладені сторінки та layout-компоненти для побудови багаторівневої структури маршрутів.
Вкладений маршрут — це маршрут, який знаходиться всередині іншого маршруту. Наприклад:
/dashboard
/dashboard/settings
/dashboard/profile
У Next.js з App Router структура папок у каталозі app визначає структуру URL. Кожна папка відповідає сегменту маршруту, а файл page.tsx визначає сторінку цього маршруту.
Наприклад:
app/
├── page.tsx
└── dashboard/
├── page.tsx
└── settings/
└── page.tsxЦя структура створює такі маршрути:
| Файл | URL | |---|---| | app/page.tsx | / | | app/dashboard/page.tsx | /dashboard | | app/dashboard/settings/page.tsx | /dashboard/settings |
У цьому уроці використовується App Router, який працює з каталогом app.
Створимо простий застосунок із головною сторінкою, панеллю користувача та сторінкою налаштувань.
Структура проєкту:
app/
├── dashboard/
│ ├── settings/
│ │ └── page.tsx
│ ├── layout.tsx
│ └── page.tsx
├── layout.tsx
└── page.tsxФайл app/page.tsx відповідає за маршрут /:
export default function HomePage() {
return (
<main>
<h1>Головна сторінка</h1>
<p>Вітаємо в застосунку.</p>
</main>
);
}Файл app/dashboard/page.tsx відповідає за маршрут /dashboard:
export default function DashboardPage() {
return (
<main>
<h1>Панель користувача</h1>
<p>Тут відображається основна інформація.</p>
</main>
);
}Створимо підкаталог settings усередині dashboard.
Файл app/dashboard/settings/page.tsx відповідає за маршрут /dashboard/settings:
export default function SettingsPage() {
return (
<main>
<h1>Налаштування</h1>
<p>Тут можна змінити налаштування профілю.</p>
</main>
);
}Тепер Next.js автоматично розуміє, що сторінка налаштувань є вкладеною в панель користувача.
layout.tsx містить спільний інтерфейс для сторінки та її дочірніх маршрутів.
Layout отримує спеціальний проп children. У ньому Next.js відображає поточну дочірню сторінку.
Наприклад, файл app/layout.tsx є кореневим layout. Він використовується для всіх сторінок застосунку:
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "Мій застосунок",
description: "Приклад вкладених маршрутів",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="uk">
<body>{children}</body>
</html>
);
}Кореневий layout обов’язково повинен містити елементи <html> і <body>.
Створимо файл app/dashboard/layout.tsx:
import Link from "next/link";
export default function DashboardLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<section>
<header>
<h1>Панель користувача</h1>
<nav>
<Link href="/dashboard">Огляд</Link>{" "}
<Link href="/dashboard/settings">Налаштування</Link>
</nav>
</header>
<div>{children}</div>
</section>
);
}Цей layout буде застосований до:
/dashboard
/dashboard/settings
усіх інших маршрутів, створених усередині app/dashboard
У результаті під час відкриття /dashboard/settings Next.js відобразить:
app/layout.tsx
app/dashboard/layout.tsx
app/dashboard/settings/page.tsx
Умовно результат можна уявити так:
RootLayout
└── DashboardLayout
└── SettingsPageНижче наведено набір файлів для невеликого застосунку.
app/layout.tsximport type { Metadata } from "next";
export const metadata: Metadata = {
title: "Вкладені маршрути",
description: "Приклад структури маршрутів у Next.js",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="uk">
<body>{children}</body>
</html>
);
}app/page.tsximport Link from "next/link";
export default function HomePage() {
return (
<main>
<h1>Головна сторінка</h1>
<Link href="/dashboard">Відкрити панель користувача</Link>
</main>
);
}app/dashboard/layout.tsximport Link from "next/link";
export default function DashboardLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<section>
<header>
<h1>Панель користувача</h1>
<nav>
<Link href="/dashboard">Огляд</Link>{" "}
<Link href="/dashboard/settings">Налаштування</Link>
</nav>
</header>
<hr />
{children}
</section>
);
}app/dashboard/page.tsxexport default function DashboardPage() {
return (
<main>
<h2>Огляд</h2>
<p>Це головна сторінка панелі користувача.</p>
</main>
);
}app/dashboard/settings/page.tsxexport default function SettingsPage() {
return (
<main>
<h2>Налаштування</h2>
<p>Це сторінка налаштувань.</p>
</main>
);
}Після запуску застосунку можна відкрити:
/ — головну сторінку;
/dashboard — огляд панелі користувача;
/dashboard/settings — налаштування.
Посилання Link дає змогу переходити між сторінками без повного перезавантаження браузера:
import Link from "next/link";
export default function Navigation() {
return (
<nav>
<Link href="/">Головна</Link>
<Link href="/dashboard">Панель</Link>
</nav>
);
}Layout застосовується до свого маршруту та всіх маршрутів, вкладених у його каталог.
Наприклад:
app/
├── layout.tsx
├── dashboard/
│ ├── layout.tsx
│ ├── page.tsx
│ └── settings/
│ └── page.tsx
└── about/
└── page.tsxДля маршруту /dashboard/settings застосовуються:
app/layout.tsx;
app/dashboard/layout.tsx;
app/dashboard/settings/page.tsx.
Для маршруту /about застосовується:
app/layout.tsx;
app/about/page.tsx.
app/dashboard/layout.tsx не застосовується до /about, оскільки цей маршрут не знаходиться всередині каталогу dashboard.
Можна створювати кілька рівнів layout-компонентів:
app/
├── layout.tsx
└── dashboard/
├── layout.tsx
└── settings/
├── layout.tsx
└── security/
└── page.tsxДля маршруту /dashboard/settings/security Next.js об’єднає:
app/layout.tsx
└── app/dashboard/layout.tsx
└── app/dashboard/settings/layout.tsx
└── app/dashboard/settings/security/page.tsxЦе зручно, коли кожна частина застосунку має власну навігацію:
кореневий layout — загальна структура застосунку;
layout панелі — меню панелі користувача;
layout налаштувань — меню розділів налаштувань.
page.tsx і layout.tsx мають різні роліpage.tsxpage.tsx:
представляє конкретну сторінку;
відповідає певному URL;
може бути кінцевою сторінкою маршруту.
layout.tsxlayout.tsx:
містить спільний інтерфейс;
отримує дочірній вміст через children;
обгортає сторінки та вкладені layout;
дає змогу не дублювати навігацію та інші спільні елементи.
Наприклад, навігацію панелі краще розмістити в app/dashboard/layout.tsx, а не дублювати її у кожній сторінці dashboard.
page.tsxКаталог сам по собі не створює сторінку:
app/
└── dashboard/Щоб маршрут /dashboard існував, потрібен файл:
app/dashboard/page.tsxNext.js розпізнає спеціальні файли за їхніми назвами:
page.tsx — сторінка;
layout.tsx — layout.
Файл Page.tsx або dashboard.tsx не виконає цю саму роль у маршрутизації.
childrenLayout повинен відобразити children, інакше дочірня сторінка не з’явиться:
export default function DashboardLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<section>
<nav>Навігація</nav>
{children}
</section>
);
}Якщо не додати {children}, сторінки /dashboard і /dashboard/settings не відобразять свій основний вміст.
LinkШлях у href має відповідати структурі каталогів:
<Link href="/dashboard/settings">Налаштування</Link>Для файлу app/dashboard/settings/page.tsx шлях /settings буде неправильним, оскільки він пропускає сегмент dashboard.
Файл layout.tsx не створює власний маршрут. Наприклад, app/dashboard/layout.tsx не відповідає URL /dashboard/layout.
Він лише визначає спільну оболонку для маршрутів усередині dashboard.
Створіть у панелі користувача ще одну сторінку:
app/
└── dashboard/
└── profile/
└── page.tsxВона повинна відкриватися за адресою /dashboard/profile.
Додайте посилання на неї до app/dashboard/layout.tsx:
<Link href="/dashboard/profile">Профіль</Link>Вміст сторінки може бути таким:
export default function ProfilePage() {
return (
<main>
<h2>Профіль</h2>
<p>Інформація про користувача.</p>
</main>
);
}Переконайтеся, що сторінка профілю використовує той самий layout панелі, що й сторінка налаштувань.
Каталоги всередині app формують сегменти URL.
page.tsx створює сторінку маршруту.
layout.tsx створює спільну оболонку для сторінки та її дочірніх маршрутів.
children у layout містить поточну дочірню сторінку.
Для /dashboard/settings Next.js застосовує кореневий layout, layout dashboard і сторінку settings.
Вкладені layout дають змогу будувати багаторівневу структуру навігації без дублювання коду.