# ensureStatic, navigation() і prefetch(): Next.js 16.4 дає контроль над тим, коли код реально працює

> Нові API Next.js 16.4: ensureStatic як build-time гарантія статичності, navigation() і prefetch() як явний контроль prefetch-бюджету. Розбираємо на офіційних прикладах, коли це потрібно і як не зловити падіння build.

- Автор: Юра Скиба (https://cookiesoftware.io)
- Опубліковано: 2026-10-15
- Категорія: React і Frontend
- Canonical: https://cookiesoftware.io/blog/nextjs-ensurestatic-navigation-prefetch

---

Next.js 16.4 додав три API для контролю того, коли і де виконується твій код. `ensureStatic` – build-time гарантія, що маршрут (або всі маршрути layout-а) лишається статичним: динамічний компонент у ньому валить build, а не тихо збільшує рахунок за сервер. `navigation()` виключає контент із prefetch і відкладає його до реальної навігації. `prefetch()` робить те саме для shell маршруту. Усі три – частина моделі Cache Components, яка з 16.4 [рекомендована для всіх застосунків](/blog/nextjs-16-4-cache-components).

## Проблема: оптимізація, яку ніхто не просив

Next.js дуже любить оптимізувати навігацію. Проблема починається тоді, коли «оптимізація» означає, що браузер або сервер робить роботу, яку користувач ніколи не попросить.

Два класичні сценарії:

1. **Випадкова динамічність.** Сторінка блогу була статичною, поки хтось не додав у layout компонент із `getCurrentUser()`. Build пройшов, деплой пройшов, а сторінка тихо стала request-time – з усіма наслідками для вартості й latency. Ніхто не помітив, бо нічого не зламалось. Зламався лише профіль витрат.

2. **Жадібний prefetch.** Список із 50 посилань, кожне з `prefetch` – і браузер завчасно тягне 50 повних сторінок разом із даними. UX чудовий, рахунок за compute і база даних під навантаженням – менш чудові.

16.4 дає інструменти проти обох.

## ensureStatic: build-time guardrail

Ідея проста: якщо маршрут МАЄ бути статичним, зроби це контрактом, який перевіряє build.

```tsx
export const ensureStatic = 'navigation';

export default function Page() {
  return (
    <>
      <UserAvatar /> {/* 🔴 цей компонент завалить build */}
      <Content />
    </>
  );
}
```

`UserAvatar` читає поточного користувача на request-time – і з `ensureStatic = 'navigation'` build чесно впаде замість того, щоб мовчки змінити характеристики сторінки.

Рівні три, від найсуворішого до точкового:

- **`'navigation'`** – навігації на цей маршрут ніколи не рендеряться на request-time. Жодного динамічного контенту взагалі.
- **`'prefetch'`** – лінки з явним prefetch на цей маршрут тягнуть лише статичний контент.
- **`'shell'`** – статичним гарантовано лише shell (те, що вантажиться при першому виявленні маршруту).

І найприємніше: `ensureStatic` працює не лише на сторінці, а й на layout. Один рядок у root layout:

```tsx
// app/layout.tsx
export const ensureStatic = 'navigation';
```

і кожна сторінка сайту гарантовано статична. Коли якійсь сторінці знадобиться динаміка – спускаєш гарантію на вкладені layout-и, лишаючи суворою решту. Для лендингів, блогів, документації і ecommerce-категорій це саме той guardrail, якого бракувало: архітектурне рішення «цей сайт статичний» тепер захищене механізмом, а не дисципліною команди.

## navigation(): prefetch-бюджет стає явним

Офіційний приклад із реліз-поста – поштовий застосунок. Inbox рендерить лінки на листи, сторінка листа показує сам лист і тред. Додаєш `prefetch` на лінки – і листи відкриваються миттєво, без спінера. Але тепер **кожен видимий лінк тягне весь тред заздалегідь** – навіть для листів, які користувач ніколи не відкриє.

Рішення 16.4 – винести важку частину в компонент і позначити її `navigation()`:

```tsx
import { navigation } from 'next/cache';

async function Message({ id }) {
  const message = await getMessage(id);

  return (
    <>
      <p>{message.subject}</p>
      <div>{message.body}</div>

      <Suspense fallback={<Spinner />}>
        <Thread id={id} />
      </Suspense>
    </>
  );
}

async function Thread({ id }) {
  await navigation();
  const thread = await getThread(id);
  // ...
}
```

Тепер prefetch тягне лише перший лист, а тред вантажиться тоді, коли користувач реально перейшов. `await navigation()` читається буквально: «цей код чекає справжньої навігації».

Поруч із ним – `prefetch()`: він виключає кешований контент зі shell маршруту й відкладає його до явного prefetch через `<Link prefetch>` або `useRouter().prefetch()`.

Це маленькі API з великою ідеєю: **prefetch-бюджет має бути явним**. Frontend performance давно вже не тільки про bundle size. У server-centric фреймворку prefetch – це compute, DB reads, cache hit rate і speculative робота, за яку хтось платить. Prefetch може одночасно покращити UX і збільшити backend-витрати – і тепер цей компроміс можна виражати в коді, а не в постфактум-розборі рахунку. Як це пов'язано з метриками користувача – ми детально розбирали в [статті про Core Web Vitals для React-розробника](/blog/core-web-vitals-react).

## Де нові API боляче кусаються

- **`ensureStatic = 'navigation'` на сторінці з cookies/headers.** Build упаде – і це правильно. Помилка не в API, а в тому, що сторінка вдавала статичну. Спочатку винеси динаміку в Suspense-межу з усвідомленим рішенням, потім додавай гарантію.
- **`navigation()` як «вимикач повільного».** Якщо контент повільний і при реальній навігації, виключення його з prefetch лише ховає проблему за спінером. Спочатку поклади його в `'use cache'`, потім вирішуй, чи потрібен він у prefetch.
- **Гарантія на root layout у динамічному застосунку.** Якщо половина сторінок персоналізована, `ensureStatic` на root перетворить розробку на боротьбу з build-ом. Це інструмент для сайтів, які СВІДОМО статичні.
- **Плутати `navigation()` і `prefetch()`.** Перший відкладає до навігації, другий – до явного prefetch. Якщо сумніваєшся, яка стадія тобі потрібна, подивись [розбір Stream/Cache/Block у 16.3](/blog/nextjs-instant-navigations) – модель стадій там.

## Знайди свою «майже статичну» сторінку

Візьми свій Next.js-проєкт і знайди одну сторінку, яка «мала би бути статичною»: блог, маркетинг, документація. Додай їй `export const ensureStatic = 'navigation'` і запусти build. Якщо він упав – вітаю, ти щойно знайшов випадкову динамічність, про яку не знав. Розберися, звідки вона прийшла (частіше за все – shared layout або «тимчасовий» компонент), і вирішуй усвідомлено: прибрати динаміку чи послабити гарантію до `'shell'`.

Другий крок – знайди список лінків із `prefetch` і подивись у network/server-логи, скільки даних тягнеться заздалегідь. Якщо там повні сторінки з важкими запитами – ти знайшов кандидата на `navigation()`.

Next.js 16.4 продовжує курс, який нам подобається: менше «фреймворк вирішує за тебе», більше «розробник декларує намір – фреймворк гарантує виконання».
