Пошук уроків, статей та іншого контенту
Порівняєте статичний і динамічний рендеринг та навчитеся обирати стратегію для маршруту.
У Next.js маршрут може формувати HTML:
статично — під час збірки або регенерації через певний час;
динамічно — під час обробки запиту користувача.
Це рішення приймається для маршруту, а не для всього застосунку. В одному проєкті можуть одночасно існувати статичні сторінки каталогу, динамічні сторінки профілю та сторінки з частково оновлюваними даними.
Від обраної стратегії залежать:
швидкість першої відповіді;
можливість кешування результату;
актуальність даних;
навантаження на сервер;
використання даних конкретного користувача.
За статичного рендерингу HTML готується заздалегідь:
під час next build;
або під час повторної генерації після завершення періоду ревалідації.
Готовий результат можна швидко віддавати багатьом користувачам. Це особливо ефективно для сторінок, які однакові для всіх або змінюються нечасто.
Типові приклади:
головна сторінка;
документація;
блог;
сторінка товару;
публічний каталог;
сторінка з маркетинговим контентом.
У App Router статичний маршрут можна оновлювати через певний інтервал. Такий підхід часто називають Incremental Static Regeneration, або ISR.
// app/products/page.tsx
type Product = {
id: number;
title: string;
price: number;
};
async function getProducts(): Promise<Product[]> {
const response = await fetch("https://api.example.com/products", {
next: {
revalidate: 3600,
},
});
if (!response.ok) {
throw new Error("Не вдалося завантажити товари");
}
return response.json();
}
export default async function ProductsPage() {
const products = await getProducts();
return (
<main>
<h1>Товари</h1>
<ul>
{products.map((product) => (
<li key={product.id}>
{product.title} — {product.price} грн
</li>
))}
</ul>
</main>
);
}revalidate: 3600 означає, що результат можна використовувати протягом 3600 секунд. Після цього Next.js зможе отримати свіжі дані та оновити результат маршруту.
Ця сторінка підходить для каталогу, якщо товари не повинні змінюватися миттєво після кожної зміни в базі даних.
За динамічного рендерингу маршрут формується під час запиту. Це потрібно, коли результат залежить від даних, доступних лише в момент виконання запиту.
Типові причини для динамічного рендерингу:
cookie користувача;
HTTP-заголовки;
сесія або авторизація;
персональні дані;
параметри запиту;
дані, які повинні бути найсвіжішими для кожного запиту.
Динамічний рендеринг не означає, що сервер обов’язково виконує всю роботу заново без будь-якого кешування. Він означає, що HTML маршруту не готується як один спільний статичний результат наперед.
// app/account/page.tsx
import { cookies } from "next/headers";
export default async function AccountPage() {
const cookieStore = await cookies();
const sessionId = cookieStore.get("session_id")?.value;
if (!sessionId) {
return (
<main>
<h1>Особистий кабінет</h1>
<p>Увійдіть, щоб переглянути профіль.</p>
</main>
);
}
return (
<main>
<h1>Особистий кабінет</h1>
<p>Ваш профіль доступний у цій сесії.</p>
</main>
);
}Значення cookie може бути різним для кожного користувача. Тому результат цієї сторінки не можна підготувати як один спільний HTML-файл для всіх відвідувачів.
Використання cookies() робить маршрут залежним від запиту. Аналогічно на стратегію можуть впливати headers() та інші API, які читають дані поточного запиту.
У більшості випадків Next.js може визначити стратегію автоматично. Проте іноді корисно явно зафіксувати намір.
// app/about/page.tsx
export const dynamic = "force-static";
export default function AboutPage() {
return (
<main>
<h1>Про компанію</h1>
<p>Цей вміст однаковий для всіх відвідувачів.</p>
</main>
);
}force-static вказує Next.js обробляти маршрут як статичний. Такий режим доречний для сторінки, яка не залежить від конкретного запиту.
Якщо статичний маршрут використовує дані, що можуть застарівати, можна додати:
export const revalidate = 1800;Тоді результат можна буде оновлювати кожні 30 хвилин.
// app/reports/page.tsx
export const dynamic = "force-dynamic";
export default async function ReportsPage() {
const response = await fetch("https://api.example.com/reports", {
cache: "no-store",
});
if (!response.ok) {
throw new Error("Не вдалося завантажити звіти");
}
const reports: { id: number; title: string }[] = await response.json();
return (
<main>
<h1>Звіти</h1>
<ul>
{reports.map((report) => (
<li key={report.id}>{report.title}</li>
))}
</ul>
</main>
);
}Тут використано два пов’язані, але різні налаштування:
dynamic = "force-dynamic" визначає динамічну обробку маршруту;
cache: "no-store" вказує не використовувати кеш для цього запиту даних.
Це доречно для звітів або операційних показників, які повинні завжди запитуватися заново.
Важливо не плутати:
як формується HTML маршруту;
як кешуються дані, з яких цей HTML формується.
Наприклад, сторінка може мати статичний результат, але отримувати дані з ревалідацією:
const response = await fetch("https://api.example.com/news", {
next: {
revalidate: 600,
},
});У цьому випадку дані можна оновлювати кожні 10 хвилин.
А інший маршрут може запитувати дані без кешування:
const response = await fetch("https://api.example.com/current-balance", {
cache: "no-store",
});Такий запит потрібен для інформації, яка має бути актуальною під час кожного звернення.
Вибираючи стратегію, потрібно окремо відповісти на два запитання:
Чи може HTML бути спільним для різних користувачів?
Як довго можна використовувати отримані дані?
Використовуйте статичний рендеринг, якщо:
сторінка однакова для всіх користувачів;
дані змінюються рідко;
допустима затримка перед появою оновлень;
важлива швидка відповідь;
сторінка добре підходить для CDN-кешування.
Використовуйте динамічний рендеринг, якщо:
сторінка залежить від авторизованого користувача;
результат використовує cookie або заголовки;
дані повинні бути актуальними для кожного запиту;
відповідь залежить від параметрів поточного запиту;
кешування спільного HTML може показати дані не тому користувачу.
Для кожного маршруту поставте такі запитання:
Чи однаковий результат для всіх користувачів?
Чи читає сторінка cookie або заголовки запиту?
Чи можна показувати дані, яким кілька хвилин або годин?
Чи повинні дані оновлюватися під час кожного запиту?
Чи буде небезпечно віддати закешований результат іншому користувачу?
Якщо сторінка загальна, а застарівання допустиме, обирайте статичний рендеринг із revalidate.
Якщо сторінка персональна або потребує даних конкретного запиту, обирайте динамічний рендеринг.
| Маршрут | Рекомендована стратегія | Причина | |---|---|---| | / | Статична | Вміст однаковий для більшості користувачів | | /blog | Статична з ревалідацією | Нові статті з’являються періодично | | /products | Статична з ревалідацією | Каталог не змінюється після кожного запиту | | /account | Динамічна | Залежить від сесії користувача | | /orders | Динамічна | Замовлення персональні | | /dashboard | Динамічна | Дані залежать від користувача та поточного стану системи |
Це не абсолютні правила. Наприклад, каталог товарів може бути динамічним, якщо ціна та залишки повинні бути абсолютно актуальними. Вибір залежить від вимог конкретного маршруту.
Якщо маршрут показує ім’я, замовлення або баланс користувача, його не слід робити спільним статичним результатом.
Перевірте, чи не потрапляють у сторінку:
дані сесії;
cookie;
персональні API-відповіді;
інформація, доступна лише одному користувачу.
revalidate робить дані миттєво актуальнимиrevalidate задає допустимий інтервал використання кешованого результату. Якщо вказано 3600 секунд, зміни можуть бути недоступними для відображення протягом цього періоду.
Для даних, які не можна показувати із затримкою, використовуйте динамічний запит без кешування.
force-dynamic без потребиПримусовий динамічний рендеринг не робить сторінку автоматично кращою. Він може збільшити навантаження на сервер і погіршити час відповіді.
Спочатку визначте, чи справді маршрут залежить від поточного запиту або потребує найсвіжіших даних.
"use client" визначає клієнтський компонент, але не є прямою командою для динамічного рендерингу маршруту.
Стратегія маршруту залежить від даних і API, які використовуються під час серверного формування сторінки. Клієнтський компонент може бути частиною статично згенерованої сторінки.
Персональні дані не повинні потрапляти у спільний кешований результат. Перед використанням статичного рендерингу переконайтеся, що HTML і дані не залежать від конкретного користувача.
Статичний рендеринг готує результат заздалегідь і добре підходить для загальнодоступного контенту.
Динамічний рендеринг формує маршрут під час запиту та підходить для персональних або найсвіжіших даних.
revalidate дає змогу періодично оновлювати статичний результат.
cookies(), headers() та дані поточного запиту зазвичай вимагають динамічної обробки.
force-static і force-dynamic дають змогу явно задати стратегію.
Кешування даних і стратегія рендерингу маршруту — пов’язані, але різні поняття.
Обирайте найпростішу стратегію, яка відповідає вимогам до актуальності та приватності даних.