Пошук уроків, статей та іншого контенту
Керуйте динамічним рендерингом сторінок, що залежать від запиту, cookies, headers або некешованих даних.
У Next.js сторінка може бути згенерована:
статично — під час збірки або під час фонового оновлення кешу;
динамічно — на сервері для кожного запиту.
Динамічний рендеринг потрібен, коли результат залежить від даних, відомих лише під час запиту:
параметрів URL;
cookies;
HTTP-заголовків;
авторизованого користувача;
некешованих даних із зовнішнього API;
інших значень, які можуть змінюватися між запитами.
Наприклад, сторінка профілю користувача не повинна бути однією статичною HTML-сторінкою для всіх відвідувачів. Вона має генеруватися з урахуванням cookies або даних поточної сесії.
У App Router маршрут стає динамічним, якщо серверний компонент використовує динамічні дані або явно вказує динамічну стратегію.
Основні способи:
Використати cookies() або headers() з next/headers.
Прочитати searchParams у сторінці.
Виконати запит із cache: 'no-store'.
dynamic = 'force-dynamic'cookies()Функція cookies() дає доступ до cookies поточного HTTP-запиту. Її можна використовувати, наприклад, для читання ідентифікатора сесії або налаштувань користувача.
import { cookies } from 'next/headers';
export default async function ProfilePage() {
const cookieStore = await cookies();
const sessionId = cookieStore.get('session_id')?.value;
if (!sessionId) {
return <p>Ви не авторизовані.</p>;
}
return <p>Сесію знайдено: {sessionId}</p>;
}Оскільки cookie може відрізнятися для кожного відвідувача, сторінка рендериться динамічно.
Не виводьте в інтерфейс реальні токени, ідентифікатори сесій та інші секретні значення. Приклад вище демонструє лише сам принцип.
headers()Функція headers() повертає заголовки поточного запиту. Це може бути корисно, коли серверу потрібно визначити мову, тип клієнта або іншу інформацію із запиту.
import { headers } from 'next/headers';
export default async function DevicePage() {
const requestHeaders = await headers();
const userAgent = requestHeaders.get('user-agent') ?? 'Невідомий клієнт';
return (
<main>
<h1>Інформація про клієнт</h1>
<p>User-Agent: {userAgent}</p>
</main>
);
}Значення User-Agent може бути іншим для кожного запиту, тому такий маршрут не можна безпечно підготувати як одну статичну сторінку.
searchParamsПараметри пошуку є частиною URL після знака ?.
Наприклад:
/products?category=booksУ сторінці App Router параметри доступні через властивість searchParams.
У сучасних версіях Next.js ця властивість має асинхронний тип:
type ProductsPageProps = {
searchParams: Promise<{
category?: string;
}>;
};
export default async function ProductsPage({
searchParams,
}: ProductsPageProps) {
const params = await searchParams;
const category = params.category ?? 'all';
return (
<main>
<h1>Товари</h1>
<p>Обрана категорія: {category}</p>
</main>
);
}Параметри пошуку не створюють окрему статичну сторінку для кожної можливої комбінації значень. Сторінка обробляє параметри під час запиту.
Сторінка також стає динамічною, якщо вона отримує дані через fetch із вимкненим кешуванням:
const response = await fetch('https://api.example.com/products', {
cache: 'no-store',
});Опція cache: 'no-store' означає, що Next.js не повинен використовувати збережену відповідь для цього запиту. Дані запитуються заново під час кожного рендерингу.
Це доречно для:
поточного балансу рахунку;
статусу замовлення;
приватних даних користувача;
даних, які мають бути максимально актуальними.
Водночас no-store не слід використовувати без потреби. Для даних, які можна кешувати, кешування зменшує кількість запитів до сервера та пришвидшує відповідь.
Розглянемо сторінку дашборду, яка:
читає cookie із сесією;
визначає мову з заголовка;
отримує свіжі дані з API;
рендериться для кожного запиту.
Файл app/dashboard/page.tsx:
import { cookies, headers } from 'next/headers';
type DashboardData = {
notifications: number;
tasks: number;
};
async function getDashboardData(sessionId: string): Promise<DashboardData> {
const response = await fetch('https://api.example.com/dashboard', {
headers: {
Authorization: `Bearer ${sessionId}`,
},
cache: 'no-store',
});
if (!response.ok) {
throw new Error('Не вдалося отримати дані дашборду');
}
return response.json();
}
export default async function DashboardPage() {
const cookieStore = await cookies();
const requestHeaders = await headers();
const sessionId = cookieStore.get('session_id')?.value;
const language = requestHeaders.get('accept-language')?.split(',')[0] ?? 'uk';
if (!sessionId) {
return (
<main>
<h1>Дашборд</h1>
<p>Увійдіть у систему, щоб переглянути дашборд.</p>
</main>
);
}
const data = await getDashboardData(sessionId);
return (
<main lang={language}>
<h1>Ваш дашборд</h1>
<p>Непрочитані сповіщення: {data.notifications}</p>
<p>Незавершені завдання: {data.tasks}</p>
</main>
);
}Ця сторінка динамічна з кількох причин:
cookies() читає дані поточного запиту;
headers() читає заголовки поточного запиту;
fetch має cache: 'no-store';
результат може бути різним для кожної сесії.
Іноді потрібно зробити весь сегмент маршруту динамічним, навіть якщо в його коді ще немає cookies(), headers() або no-store.
Для цього в page.tsx, layout.tsx або іншому файлі сегмента можна вказати:
export const dynamic = 'force-dynamic';
export default async function ReportsPage() {
return (
<main>
<h1>Звіти</h1>
<p>Ця сторінка рендериться на сервері для кожного запиту.</p>
</main>
);
}force-dynamic корисний, коли:
динамічність є вимогою маршруту;
дані отримуються не через стандартний fetch;
потрібно явно зафіксувати поведінку маршруту;
важливо не дозволити Next.js обрати статичну генерацію.
Використовуйте його свідомо: примусова динамічність може збільшити навантаження на сервер.
Це різні поняття:
динамічний рендеринг визначає, коли формується сторінка;
кешування даних визначає, чи можна повторно використати результат конкретного запиту.
Наприклад, сторінка може бути динамічною через cookie, але один із її запитів може використовувати кешовані загальні дані. І навпаки, один некешований запит може зробити маршрут динамічним.
Якщо дані залежать від поточного користувача, запит зазвичай потрібно виконувати з урахуванням його сесії та не використовувати спільний кеш для всіх користувачів.
const response = await fetch('https://api.example.com/me', {
headers: {
Authorization: `Bearer ${sessionId}`,
},
cache: 'no-store',
});Так персональні дані не будуть помилково повторно використані для іншого запиту.
Динамічний рендеринг виконується на сервері. Для цього не потрібно додавати 'use client'.
Серверна сторінка може:
читати cookies;
читати headers;
звертатися до внутрішніх API;
виконувати запити до бази даних;
повертати готову розмітку.
'use client' потрібен для інтерактивності в браузері, наприклад для обробників подій або стану React. Додавання цієї директиви саме по собі не є способом увімкнути динамічний рендеринг.
cookies() або headers() без awaitУ сучасних версіях Next.js ці API потрібно використовувати асинхронно:
const cookieStore = await cookies();
const requestHeaders = await headers();Не слід покладатися на синхронний варіант виклику.
Якщо відповідь залежить від користувача, не використовуйте для неї спільний кеш без чіткого розуміння наслідків.
// Небезпечно для персональних даних без правильної стратегії кешування
const response = await fetch('https://api.example.com/me');Для даних поточного користувача явно вкажіть:
const response = await fetch('https://api.example.com/me', {
cache: 'no-store',
});next/headerscookies() і headers() призначені для серверного коду. Їх не можна використовувати в компоненті з 'use client'.
Зчитуйте такі значення у серверному компоненті, а потім передавайте в клієнтський компонент лише ті дані, які справді потрібно показати або використати в браузері.
Не потрібно додавати dynamic = 'force-dynamic' до кожного маршруту. Якщо сторінка не залежить від запиту та її дані можна кешувати, статичний рендеринг часто буде швидшим і дешевшим.
Cookie можуть містити токени, ідентифікатори сесій або інші секрети. Не передавайте їх у JSX, URL або клієнтський JavaScript.
Оберіть динамічний рендеринг, якщо:
HTML залежить від авторизованого користувача;
сторінка використовує cookies або headers;
дані повинні бути свіжими під час кожного запиту;
URL-параметри змінюють результат сторінки;
відповідь не можна безпечно зберігати як спільну статичну сторінку.
Залишайте статичний рендеринг, якщо:
вміст однаковий для всіх користувачів;
дані змінюються рідко;
сторінку можна безпечно кешувати;
немає залежності від поточного запиту.
Динамічний рендеринг генерує сторінку на сервері під час запиту.
cookies() і headers() роблять маршрут залежним від поточного HTTP-запиту.
searchParams дає змогу формувати сторінку на основі параметрів URL.
fetch з cache: 'no-store' отримує дані без використання кешу.
dynamic = 'force-dynamic' примусово вмикає динамічний рендеринг для сегмента маршруту.
Динамічність маршруту та кешування окремих даних — різні механізми.
Персональні дані потрібно отримувати обережно, щоб не поділитися ними між користувачами.