Next.js 16.4 (вийшов 6 жовтня) офіційно рекомендує Cache Components як найкращу модель для кожного застосунку: нові проєкти через create-next-app отримують їх увімкненими, а в Next.js 17 це стане default для всіх. Якщо коротко, фреймворк остаточно йде від моделі «вгадай, кешується цей fetch чи ні» до моделі, де кеш – явна, декларативна властивість частини дерева компонентів. Це хороший напрям. Але слово «default» не означає «включи й забудь» – розбираємось, що реально змінюється і як мігрувати без сюрпризів.
Що саме ввімкнули в 16.4
Найважливіша зміна 16.4 – не чергові відсотки швидкості Turbopack, а офіційна позиція команди. Дослівно з анонсу: «Starting with Next.js 16.4, we're excited to recommend Cache Components as the best choice for every Next.js app». До цього релізу рекомендація не була універсальною: у деяких сценаріях нова модель не давала тих самих гарантій вартості й продуктивності, що стара. 16.4 закриває ці прогалини, а Next.js 17 зробить модель дефолтною вже для всіх.
Сьогодні Cache Components вмикаються двома прапорцями в конфігу:
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
cacheComponents: true,
partialPrefetching: true,
};
export default nextConfig;Зверни увагу: Partial Prefetching тепер офіційно частина моделі, а не окрема опція. Як він працює, ми детально розбирали в статті про Instant Navigations.
Ментальна модель: кеш як властивість компонента
Команда Next.js пропонує думати про 'use cache' як про компонентну версію HTTP-заголовка Cache-Control:
import { cacheLife } from 'next/cache';
export default async function DashboardPage() {
const currentUser = await getCurrentUser();
return (
<div>
<p>Вітаю, {currentUser.name}</p>
<Suspense fallback={<Loading />}>
<Projects userId={currentUser.id} />
</Suspense>
</div>
);
}
async function Projects({ userId }: { userId: string }) {
'use cache';
cacheLife('hours');
return db.query.projects.findMany({
where: eq(projectsTable.userId, userId),
});
}Одна сторінка поєднує персоналізовану request-time частину (привітання з іменем) і кешований UI (список проєктів живе годинами). Це ближче до того, як продуктовий інженер реально думає: не «ця route статична чи динамічна?», а «яка конкретно частина UI потребує свіжих даних?».
Семантику тегів, revalidateTag/updateTag і read-your-own-writes ми докладно розбирали в ментальній моделі кешу Next.js – вона лишається чинною, 16.4 нічого в ній не ламає, лише додає гарантій зверху.
ensureStatic: build-time guardrail замість тихої деградації
Найнедооціненіша фіча релізу. Стара болячка App Router: один випадково доданий динамічний компонент тихо міняв характеристики цілої сторінки – і твій «статичний» блог раптом рендериться на кожен request, а рахунок за compute росте.
Тепер це можна заборонити на рівні build:
export const ensureStatic = 'navigation';
export default function Page() {
return (
<>
<UserAvatar /> {/* 🔴 build впаде: динаміка у статичній route */}
<Content />
</>
);
}Значення три, за рівнем суворості: 'navigation' (навігації на route ніколи не рендеряться на request), 'prefetch' (префетчі тягнуть лише статичне) і 'shell' (статичним гарантовано лише первинний shell). Можна ставити на layout – і тоді гарантія поширюється на всі сторінки під ним, аж до «увесь сайт статичний, окремі розділи – виняток».
Для блогу, маркетингових сторінок, документації чи ecommerce-категорій це саме той архітектурний контракт, який раніше жив тільки в головах: тепер його порушення ловить CI, а не рахунок наприкінці місяця.
navigation() і prefetch(): явний бюджет на спекулятивну роботу
Prefetch – чудова річ, поки список із 50 посилань не вирішив завчасно завантажити пів бази даних. Класичний приклад з анонсу – inbox: хочеться префетчити перший екран повідомлення, але не тягнути entire thread для кожного видимого лінка.
import { navigation } from 'next/cache';
async function Thread({ id }: { id: string }) {
await navigation(); // виключає компонент із префетчу
const thread = await getThread(id);
// рендериться лише після реальної навігації
}await navigation() каже: «цю частину не вантаж на prefetch, дочекайся справжнього переходу». Симетричний await prefetch() робить навпаки: виключає вміст із shell, відкладаючи його до явного префетчу.
Маленькі API з великою ідеєю: спекулятивна робота має бути явним бюджетом. Frontend performance давно не лише про bundle size – у server-centric фреймворку prefetch може покращити UX і одночасно роздути backend cost. Тепер цей компроміс контрольований.
Як зламати собі кеш у перший день
- Масове додавання
'use cache'у день міграції. Спочатку розклади сторінки на три кошики: глобальні рідкозмінні дані, користувацькі повторно використовувані, request-specific/security-sensitive. Кешові межі малюються після цього, не до. - Кешування того, що вимагає read-your-own-writes. Користувач зберіг налаштування і бачить старі – це баг довіри, а не performance.
- Ігнорування
ensureStaticна «очевидно статичних» сторінках. Саме там він найдешевший: один рядок сьогодні проти тихої деградації через пів року. - Сліпе оновлення у день релізу. 16.4 стабільний, але питання в твоєму коді: у code review тепер питай не «чому це dynamic?», а «який freshness contract у цієї частини UI?».
Тест-драйв на власному route
Візьми свій найбільш «гібридний» route (трохи персоналізації + багато спільного контенту) і випиши для кожної секції UI її freshness contract: скільки даним можна жити і хто їх інвалідує. Потім перевір, чи відповідає цьому фактична поведінка. Б'юсь об заклад, знайдеш щонайменше одну секцію, що рендериться на кожен request без жодної причини.
Найцікавіше в 16.4 – навіть не API, а напрям: App Router стає менш «framework decides» і більш «developer declares intent». Для тих, хто готується до співбесід із Next.js, базовий огляд моделі – у гайді для React-розробника.