Пошук уроків, статей та іншого контенту
Зрозумієте принципи App Router і навчитеся організовувати сторінки та маршрути в сучасному Next.js.
App Router — це сучасний спосіб організації маршрутів у Next.js. Він використовує папку app і файлову структуру проєкту для визначення URL-адрес.
Кожен маршрут складається з папки, а спеціальний файл page.js або page.tsx визначає вміст сторінки.
Наприклад:
app/
├── page.js
├── about/
│ └── page.js
└── products/
├── page.js
└── [id]/
└── page.jsЦя структура створює такі маршрути:
app/page.js → /
app/about/page.js → /about
app/products/page.js → /products
app/products/[id]/page.js → /products/значення-id
На відміну від звичайного React-застосунку, не потрібно вручну налаштовувати маршрути за допомогою окремої бібліотеки. Next.js визначає їх на основі файлів і папок.
Мінімальна сторінка в App Router — це файл page.js усередині папки app.
// app/page.js
export default function HomePage() {
return (
<main>
<h1>Головна сторінка</h1>
<p>Ласкаво просимо до мого Next.js-застосунку!</p>
</main>
);
}Функція сторінки повинна бути експортована як default. Її результат — JSX, який буде відображено в браузері.
Щоб створити сторінку /about, потрібно додати файл:
// app/about/page.js
export default function AboutPage() {
return (
<main>
<h1>Про нас</h1>
<p>Це сторінка з інформацією про застосунок.</p>
</main>
);
}Тепер сторінка буде доступна за адресою:
http://localhost:3000/aboutПапки всередині app відповідають частинам URL.
Створимо сторінку каталогу:
// app/products/page.js
const products = [
{ id: 1, name: 'Ноутбук' },
{ id: 2, name: 'Навушники' },
];
export default function ProductsPage() {
return (
<main>
<h1>Товари</h1>
<ul>
{products.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>
</main>
);
}Файл app/products/page.js відповідає маршруту /products.
Вкладеність папок безпосередньо відображається у вкладеності URL:
app/
└── dashboard/
└── settings/
└── page.jsТакий файл створює маршрут:
/dashboard/settingsСамі назви папок не відображаються на сторінці автоматично. Вони лише визначають адресу маршруту.
Файл layout.js або layout.tsx визначає спільну структуру для сторінки та її дочірніх маршрутів.
Кореневий layout розташований у папці app:
// app/layout.js
import Link from 'next/link';
export default function RootLayout({ children }) {
return (
<html lang="uk">
<body>
<header>
<nav>
<Link href="/">Головна</Link>{' '}
<Link href="/about">Про нас</Link>{' '}
<Link href="/products">Товари</Link>
</nav>
</header>
{children}
</body>
</html>
);
}children — це поточна сторінка або вкладений layout.
Наприклад:
для / у children буде вміст app/page.js;
для /about у children буде вміст app/about/page.js;
для /products у children буде вміст app/products/page.js.
Кореневий layout є спільним для всіх сторінок застосунку. Саме тут зазвичай розміщують:
основну навігацію;
загальний header;
footer;
тег html;
тег body.
Для переходів між маршрутами використовується компонент Link з next/link.
import Link from 'next/link';
export default function HomePage() {
return (
<main>
<h1>Головна сторінка</h1>
<Link href="/about">Перейти на сторінку «Про нас»</Link>
</main>
);
}Link дає Next.js змогу виконувати навігацію без повного перезавантаження сторінки.
Для внутрішніх маршрутів застосунку використовуйте:
<Link href="/products">Товари</Link>Замість звичайного HTML-елемента:
<a href="/products">Товари</a>Звичайний тег a також працює, але зазвичай спричиняє повне завантаження документа. Для внутрішньої навігації в Next.js рекомендовано використовувати Link.
Іноді частина URL повинна змінюватися. Наприклад, сторінки окремих товарів можуть мати такі адреси:
/products/1
/products/2
/products/42Для цього назву папки записують у квадратних дужках:
app/
└── products/
└── [id]/
└── page.jsУ файлі сторінки значення id доступне через параметр params:
// app/products/[id]/page.js
export default async function ProductPage({ params }) {
const { id } = await params;
return (
<main>
<h1>Товар</h1>
<p>Ідентифікатор товару: {id}</p>
</main>
);
}У сучасних версіях Next.js params може бути асинхронним значенням, тому в прикладі використовується await params.
Якщо користувач відкриє адресу:
/products/42сторінка покаже:
Ідентифікатор товару: 42Те саме правило працює для інших маршрутів:
app/blog/[slug]/page.jsЦей файл відповідає адресам на кшталт:
/blog/nextjs-introduction
/blog/app-routerЗначення slug можна отримати так:
export default async function BlogPostPage({ params }) {
const { slug } = await params;
return <h1>Стаття: {slug}</h1>;
}Компоненти в папці app за замовчуванням є серверними компонентами.
Це означає, що вони виконуються на сервері, а не в браузері. Серверні компоненти зручно використовувати для:
відображення статичного вмісту;
отримання даних на сервері;
формування HTML для сторінки.
Для звичайної сторінки не потрібно додавати спеціальний рядок:
export default function Page() {
return <h1>Сторінка</h1>;
}Якщо компоненту потрібні обробники подій або браузерні можливості, на початку файлу додають директиву 'use client':
'use client';
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Натиснуто: {count}
</button>
);
}Директива 'use client' повинна бути першим рядком файлу, перед імпортами.
Для розуміння маршрутизації достатньо запам’ятати:
сторінки в app за замовчуванням є серверними;
інтерактивні компоненти з useState, onClick та іншими браузерними можливостями повинні бути клієнтськими;
'use client' потрібно додавати лише там, де це справді необхідно.
App Router використовує спеціальні назви файлів для різних частин маршруту:
page.js — вміст сторінки;
layout.js — спільний layout для маршруту та його дочірніх маршрутів;
loading.js — стан завантаження;
error.js — обробка помилок;
not-found.js — сторінка для випадку, коли ресурс не знайдено.
Для початку найважливішими є page.js і layout.js.
Наприклад, маршрут із layout може мати таку структуру:
app/
├── layout.js
├── page.js
└── dashboard/
├── layout.js
├── page.js
└── settings/
└── page.jsУ цьому випадку:
app/layout.js використовується всім застосунком;
app/dashboard/layout.js використовується сторінкою dashboard та її дочірніми маршрутами;
app/dashboard/page.js відповідає /dashboard;
app/dashboard/settings/page.js відповідає /dashboard/settings.
Layout-и вкладаються один в одного. Для /dashboard/settings Next.js використовує:
кореневий layout;
layout для dashboard;
сторінку settings.
Розглянемо невеликий застосунок із головною сторінкою, сторінкою про команду та каталогом товарів.
Структура файлів:
app/
├── layout.js
├── page.js
├── about/
│ └── page.js
└── products/
├── page.js
└── [id]/
└── page.jsКореневий layout:
// app/layout.js
import Link from 'next/link';
export default function RootLayout({ children }) {
return (
<html lang="uk">
<body>
<header>
<nav>
<Link href="/">Головна</Link>{' | '}
<Link href="/about">Про нас</Link>{' | '}
<Link href="/products">Товари</Link>
</nav>
</header>
<hr />
{children}
</body>
</html>
);
}Головна сторінка:
// app/page.js
import Link from 'next/link';
export default function HomePage() {
return (
<main>
<h1>Магазин</h1>
<p>Оберіть розділ сайту.</p>
<Link href="/products">Переглянути товари</Link>
</main>
);
}Сторінка товарів:
// app/products/page.js
import Link from 'next/link';
const products = [
{ id: 1, name: 'Ноутбук' },
{ id: 2, name: 'Навушники' },
{ id: 3, name: 'Клавіатура' },
];
export default function ProductsPage() {
return (
<main>
<h1>Товари</h1>
<ul>
{products.map((product) => (
<li key={product.id}>
<Link href={`/products/${product.id}`}>
{product.name}
</Link>
</li>
))}
</ul>
</main>
);
}Сторінка окремого товару:
// app/products/[id]/page.js
export default async function ProductPage({ params }) {
const { id } = await params;
return (
<main>
<h1>Сторінка товару</h1>
<p>Ви переглядаєте товар з ідентифікатором: {id}</p>
</main>
);
}Сторінка /about:
// app/about/page.js
export default function AboutPage() {
return (
<main>
<h1>Про нас</h1>
<p>Ми створюємо прості та корисні вебзастосунки.</p>
</main>
);
}Цей приклад демонструє основний принцип App Router: маршрут визначається розташуванням і назвою файлів.
page.jsПапка сама по собі не створює сторінку:
app/about/Потрібен спеціальний файл:
app/about/page.jsБез page.js маршрут /about не матиме сторінки.
Сторінка повинна мати експорт за замовчуванням:
export default function Page() {
return <h1>Сторінка</h1>;
}Такий варіант не створить коректну сторінку:
export function Page() {
return <h1>Сторінка</h1>;
}Якщо папка називається [id], параметр також називається id:
app/products/[id]/page.jsexport default async function Page({ params }) {
const { id } = await params;
return <p>{id}</p>;
}Якщо папка називається [slug], потрібно отримувати slug, а не id.
children у layoutLayout повинен відображати children, інакше вкладені сторінки не з’являться:
export default function Layout({ children }) {
return (
<div>
{children}
</div>
);
}useState без 'use client'Компонент із React-станом повинен починатися з директиви:
'use client';
import { useState } from 'react';Без неї Next.js повідомить про помилку під час використання клієнтських можливостей у серверному компоненті.
<a> для всіх внутрішніх переходівДля переходів між сторінками власного застосунку краще використовувати Link:
import Link from 'next/link';
<Link href="/about">Про нас</Link>App Router використовує папку app для організації маршрутів.
Файл page.js створює сторінку.
Вкладені папки створюють вкладені URL.
layout.js містить спільну структуру сторінок.
Компонент Link використовується для внутрішньої навігації.
Динамічні сегменти записуються у квадратних дужках, наприклад [id].
Значення динамічного сегмента доступне через params.
Компоненти в app за замовчуванням є серверними.
Для інтерактивності використовується директива 'use client'.