Next.jsMiddleTechnical
Что такое PPR (Partial Prerendering) в Next.js 15 и как его модель меняется с Cache Components в Next.js 16?
PPR в Next.js 15 рендерит статическую оболочку при билде и стримит динамические Suspense-«дыры» при запросе. В Next.js 16 Cache Components заменяют неявную статику явной директивой use cache.
Partial Prerendering (PPR) в Next.js 15
PPR — это гибридная модель рендеринга, при которой одна страница сочетает статическую оболочку (shell) с динамическими «дырами» (holes). Статическая часть рендерится при билде и отдаётся мгновенно из CDN, а динамические части стримятся параллельно с сервера после того, как соединение установлено.
Как включить PPR в Next.js 15
// next.config.ts
import type { NextConfig } from 'next';
const config: NextConfig = {
experimental: {
ppr: 'incremental', // 'incremental' или true для всего приложения
},
};
export default config;
Разметка страницы с PPR
// app/shop/page.tsx
import { Suspense } from 'react';
import { unstable_noStore as noStore } from 'next/cache';
// Статическая часть — рендерится при билде
function ProductShell() {
return (
<div>
<h1>Магазин</h1>
<nav>Категории</nav>
</div>
);
}
// Динамическая часть — рендерится на сервере при каждом запросе
async function PersonalizedCart() {
noStore(); // сигнал: эта ветка динамическая
const cart = await fetch('https://api.example.com/cart', {
cache: 'no-store',
}).then(r => r.json());
return <div>Корзина: {cart.items} товаров</div>;
}
export const experimental_ppr = true; // включить PPR для этой страницы
export default function ShopPage() {
return (
<>
<ProductShell />
{/* Suspense boundary = «дыра» для динамического контента */}
<Suspense fallback={<div>Загрузка корзины...</div>}>
<PersonalizedCart />
</Suspense>
</>
);
}
Как PPR меняется с Cache Components в Next.js 16
В Next.js 16 появляются Cache Components — компоненты с директивой use cache на уровне функции или файла. Они кардинально меняют подход: вместо «статическая оболочка + динамические дыры» приходит модель «всё потенциально динамическое, но явно закэшированное».
- PPR в Next.js 15: компилятор сам определяет статические части по отсутствию динамических API (
cookies(),headers(),noStore()). Граница задаётсяSuspense. - Cache Components в Next.js 16: разработчик явно помечает, что кэшировать с помощью
use cache. Это даёт точный контроль над TTL и инвалидацией.
// Next.js 16: Cache Component
async function CachedProductList() {
'use cache'; // директива кэширования
const products = await fetchProducts();
return <ProductGrid products={products} />;
}
export default function ShopPage() {
return (
<>
<CachedProductList /> {/* рендерится из кэша, не статически при билде */}
<Suspense fallback={<Spinner />}>
<LiveCart /> {/* всегда динамический */}
</Suspense>
</>
);
}
Ключевые отличия моделей
- PPR (Next.js 15): статическая оболочка определяется на этапе билда; динамичность = отсутствие кэша.
- Cache Components (Next.js 16): кэш — это явная аннотация; компонент может быть динамическим по умолчанию, но закэшированным при следующем рендере.
- Cache Components поддерживают параметры TTL и теги инвалидации прямо в директиве.
Подводные камни
- PPR работает только с App Router: Pages Router не поддерживает PPR ни в какой форме.
- experimental_ppr = true нужен на каждой странице при incremental режиме — иначе страница использует обычный рендеринг.
- Suspense boundary обязателен: без
<Suspense>вокруг динамического компонента PPR не создаст «дыру» — страница откатится к полностью динамическому рендерингу. - use cache (Next.js 16) — экспериментальная директива: поведение может меняться до стабильного релиза.
- Данные пользователя в статической оболочке: случайно включив personalized данные в статическую часть, вы закэшируете их для всех пользователей.
- CDN-инвалидация: статическая оболочка кэшируется на CDN; при деплое нового кода старый shell может сохраняться до инвалидации.
Common mistakes
- Отвечать определением без production-сценария.
- Не называть runtime boundary, security boundary или failure mode.
- Игнорировать версию API, observability и тестовую проверку.
What the interviewer is testing
- Объясняет механизм своими словами и без выдуманных API.
- Называет реальные риски, диагностику и критерий корректности.
- Связывает ответ с текущей документацией и миграционными ограничениями.