# Next.js 16.3 Instant Navigations: як отримати SPA-відчуття без відмови від Server Components

> Stream, Cache або Block: розбираємо нову ментальну модель навігацій у Next.js 16.3 – reusable shell замість prefetch-на-кожен-лінк, Partial Prefetching, instant() у Playwright і чесні trade-offs.

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

---

**Коротка відповідь.** Instant Navigations у Next.js 16.3 – це не одна фіча, а нова дисципліна: кожен маршрут, який чекає на дані, тепер має явно обрати один із трьох станів. **Stream** – користувач миттєво бачить лоадер через `<Suspense>`; **Cache** – миттєво бачить кешований UI через `'use cache'`; **Block** – `export const instant = false`, і навігація чесно чекає сервер. Плюс переписаний prefetch: замість запиту на кожен лінк у viewport – один reusable shell на маршрут. Усе вмикається флагами `cacheComponents: true` і `partialPrefetching: true`, які в майбутньому мажорі стануть дефолтом.

## Проблема, яку нарешті визнали

Класична навігація в server-driven застосунку: клік → нічого → відповідь сервера → сторінка. Для блогу це нормально. Для застосунку – ні: саме через оцей «нічого» багато команд тікали в SPA, жертвуючи перевагами серверної моделі.

SPA вирішує це так: клік → миттєво каркас наступної сторінки → дані доїжджають. Next.js 16.3 переносить цю механіку в server-first модель – без відмови від Server Components.

## Stream, Cache або Block: ментальна модель

Після ввімкнення `cacheComponents: true` кожен `await` даних у маршруті – це вибір:

```ts
// next.config.ts
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  cacheComponents: true,
  partialPrefetching: true,
};

export default nextConfig;
```

**Stream.** Обгортаєш повільну частину в `<Suspense>` – користувач миттєво отримує loading state, контент дострумлюється. Навігація інстантна, дані свіжі, але користувач бачить скелетон.

**Cache.** Позначаєш функцію `'use cache'` – користувач миттєво бачить попередньо закешований UI. Навігація інстантна І з контентом, але контент може бути несвіжим до revalidation.

**Block.** Іноді ти СВІДОМО хочеш почекати сервер – наприклад, блог, який не хоче показувати скелетон замість статті:

```ts
// page.tsx
export const instant = false;
```

Ключова зміна філософії: повільна навігація в dev – тепер **помилка** (Instant Insights автоматично підсвічує маршрути, які блокують), а не тихе «ну, так вийшло». Block лишається легальним станом, але явним рішенням, а не дефолтом через забутий Suspense.

Як обирати? Дуже схоже на [вибір порогів у кешуванні взагалі](/blog/redis-koly-potriben-kesh): питання не «що швидше», а «хто володіє freshness». Котирування акцій – Stream (свіжість критична). Сайдбар чатів – Cache (учорашня назва чату нікого не вб'є). Юридичний документ – можливо, чесний Block.

## Partial Prefetching: shell на маршрут, а не запит на лінк

До 16.3 Next.js слав prefetch-запит на кожен `<Link>` у viewport. Сайдбар із 20 чатами = 20 запитів, навіть якщо всі ведуть на той самий маршрут `/chat/[id]`. Команда сама визнає в анонсі: «виглядало безглуздо, і, чесно кажучи, ми згодні».

Нова модель запозичена у SPA: **prefetch-иться один reusable shell на маршрут**, кешується на клієнті на всю сесію і реюзається між усіма лінками цього маршруту. Концептуально – як per-route code splitting, тільки для UI-каркаса.

Якщо для конкретного лінка хочеться більше за shell (скажімо, хедер чату має «впасти» в UI миттєво) – `<Link prefetch={true}>` вмикає глибший per-link prefetch, але і він рендерить лише те, що доступно синхронно, відомо з URL (params/searchParams) або позначено `'use cache'`. Тобто вибір більше не «все або нічого»: shell – базовий рівень, точковий prefetch – надбудова.

Бонус на майбутнє з анонсу: оскільки shells кешуються, команда досліджує offline-навігацію – маршрути, якими можна ходити при короткій втраті мережі.

## Що з state між переходами

Shell реюзається, тому важливо розуміти межі: layout-и, які не змінились, зберігають свій React-state (це поведінка App Router, не новинка 16.3), а от усе, що в shell відрендерено «порожнім», при фактичній навігації добирає дані. Практичний наслідок: стан, який має пережити навігацію, живе в layout або в URL (searchParams), а не в page-компоненті – Instant Navigations роблять цю стару пораду ще актуальнішою, бо тепер між shell і повним контентом з'являється видима пауза, яку ти проєктуєш свідомо.

## Як це тестувати, а не «відчувати на око»

Найнедооціненіша частина релізу – тестовий хелпер `instant()` для Playwright:

```ts
import { expect, test } from '@playwright/test';
import { instant } from '@next/playwright';

test('заголовок товару видно миттєво', async ({ page }) => {
  await page.goto('/products/shoes');

  // Асерти про те, що видно БЕЗ очікування мережі
  await instant(page, async () => {
    await page.click('a[href="/products/hats"]');
    await expect(page.locator('h1')).toContainText('Baseball Cap');
    await expect(page.getByText('Перевіряємо наявність…')).toBeVisible();
  });

  // А це вже після відповіді сервера
  await expect(page.getByText('12 в наявності')).toBeVisible();
});
```

Це переводить «навігація стала повільною після рефакторингу» з категорії скарг користувачів у категорію червоних тестів у CI. Додатково в DevTools з'явився Navigation Inspector – пауза навігації «на shell», щоб очима побачити, що саме prefetch-иться для маршруту.

## Чесні trade-offs

- **Пам'ять клієнта.** Shells кешуються на сесію – для застосунку з сотнями маршрутів це пам'ять у браузері. Команда каже, що оптимізує prefetch далі; міряй свій кейс.
- **Freshness.** `'use cache'` – це кеш з усіма наслідками: stale UI, стратегія інвалідації, read-your-own-writes після мутацій. Інстантність не безкоштовна – вона куплена або скелетоном, або несвіжістю.
- **Складність.** Три стани × кожен маршрут = матриця рішень, якої раніше не було. Для лендинга це оверкіл; для продукту з щільною навігацією – саме та інженерія, якої бракувало.
- **Статус tooling.** В анонсі чесно перелічені known issues прев'ю (зокрема глюки Instant Insights у Safari – в dev радять Chrome/Firefox). Стабільний 16.3 вийшов 3 серпня 2026; семантику конкретних флагів перевіряй по актуальних docs свого мінора.

Команда Vercel обкатувала це на власному v0 і показує графік прискорення навігацій – але без абсолютних чисел у тексті, тож і я їх не наводитиму: запусти Instant Insights на своєму проєкті, він сам підсвітить твої повільні маршрути.

## Типові помилки

1. **Увімкнути флаги і вважати справу зробленою.** Флаги лише вмикають діагностику й нову модель – рішення Stream/Cache/Block по кожному маршруту ухвалюєш ти.
2. **Cache як дефолт для всього**, бо «так інстантніше». Несвіжий баланс рахунку гірший за секунду скелетона.
3. **`export const instant = false` як спосіб «заткнути» Instant Insights** – це легальний інструмент для свідомого блокування, а не глушилка діагностики.
4. **Ігнорувати `instant()`-тести.** Без них перший же рефакторинг layout-а тихо поверне повільні навігації.

## Практичне завдання

Візьми свій Next.js-проєкт (або створи демо «каталог → сторінка товару») і пройди цикл: увімкни `cacheComponents` + `partialPrefetching` → подивись, які маршрути Instant Insights позначить повільними → для кожного ухвали явне рішення Stream/Cache/Block з однією фразою обґрунтування → закрий найважливішу навігацію `instant()`-тестом. Це рівно той воркфлоу, який тепер відрізняє «я чув про 16.3» від «я вмію ним користуватись».

Контекст ширше: базовий огляд рендерингу й кешування в Next.js – у [гайді для співбесід](/blog/nextjs-dlia-react-rozrobnyka), про вимірювання перформансу React без карго-культу – в [розборі оптимізації](/blog/optymizatsiia-react-performance), а про те, як TypeScript 7 прискорює сам цикл розробки, – у [свіжому розборі Go-компілятора](/blog/typescript-7-go-compiler).
