---
title: "Next.js prefetch zmierzony: Partial Prefetching w 16.3"
description: "Partial Prefetching w Next.js 16.3 zmniejszył w naszym benchmarku prefetch nawet o 93%, ale treść adresu przychodzi po kliknięciu. Kiedy co się opłaca."
canonical_url: "https://www.outofplace.space/pl/blog/nextjs-prefetch-benchmark"
language: "pl"
last_updated: "2026-09-23"
alternates:
  en: "https://www.outofplace.space/blog/nextjs-prefetch-benchmark.md"
---

# Next.js prefetch zmierzony: Partial Prefetching w 16.3

> Partial Prefetching w Next.js 16.3 zmniejszył w naszym benchmarku prefetch nawet o 93%, ale treść adresu przychodzi po kliknięciu. Kiedy co się opłaca.

Opublikowano 2026-09-23 · Kategoria: Wydajność · Tagi: Next.js, Prefetching, React

[Next.js 16.3](https://nextjs.org/blog/next-16-3) zmienia to, czym jest Next.js prefetch. Z włączonym [`partialPrefetching`](https://nextjs.org/docs/app/api-reference/config/next-config-js/partialPrefetching) App Router nie pobiera już payloadu dla każdego linku na ekranie: pobiera jeden wspólny szkielet na trasę, a treść zależną od adresu zostawia na po kliknięciu. Zbudowaliśmy jedną aplikację na sześć sposobów i przeklikaliśmy ją 3744 razy, żeby sprawdzić, co ta zamiana daje i ile kosztuje.

Na stronie dokumentacji ze 100 linkami w bocznym menu prefetch zmalał o 93%. Ceną jest czas: treść, która wcześniej była na ekranie niemal od razu, przychodzi teraz kilkaset milisekund po kliknięciu, za szkieletem strony. Która strona tej zamiany jest lepsza, zależy od strony i od [jednego szczegółu Reacta, który łatwo przeoczyć](#szkielet-ktory-zobaczysz-300-ms).

- Partial Prefetching zmniejszył prefetch o 78–93% na stronach z wieloma linkami, a całą nawigację w dokumentacji mniej więcej jedenastokrotnie.
- Szkielet pojawia się od razu. Treść zależna od adresu przychodzi po kliknięciu: 345 ms w dokumentacji wobec 52 ms przy samym Cache Components, na zwolnionym telefonie.
- Szkielet, który się pojawi, zostaje na około 300 ms, bo React tyle wstrzymuje każde odsłonięcie.
- Linki z `prefetch={true}` zostają natychmiastowe w każdej konfiguracji.
- `prefetchInlining` zostaw domyślny; HTTP/2 nie zmienił niczego ważnego.

## Co zmienia Partial Prefetching

Do wersji 16.3 [App Router](https://nextjs.org/docs/app) robił prefetch dla każdego linku: gdy na ekran wjeżdżało N linków, pobierał mniej więcej N payloadów. Z `partialPrefetching` (wymaga [`cacheComponents`](https://nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents)) pobiera jeden [App Shell](https://nextjs.org/docs/app/guides/adopting-partial-prefetching) na trasę: wszystko, co strona renderuje niezależnie od adresu. Linki do tej samej trasy dzielą ten jeden prefetch. Treść, która czyta `params` albo `searchParams`, rozwiązuje się po kliknięciu, chyba że link poprosi o więcej przez `prefetch={true}`.

Porównaliśmy sześć buildów tej samej aplikacji:

- **L0**, bez Cache Components: klasyczny App Router.
- **C0**, samo Cache Components.
- **P0**, Cache Components z Partial Prefetching i domyślnym inliningiem prefetchu.
- **P1–P3**, to samo z wyłączonym inliningiem, z małymi progami i z dużymi.

Każdy przeklikaliśmy w trzech nawigacjach: z katalogu 48 produktów na stronę produktu, ze strony dokumentacji ze 100 linkami w menu na inną stronę dokumentacji i z wyników wyszukiwania na inne wyszukiwanie przez jeden z ośmiu linków z `prefetch={true}`.

## Bajty prefetchu: nawet o 93% mniej

W dokumentacji samo Cache Components pobierało z wyprzedzeniem każdy widoczny link z menu. Partial Prefetching zmienił to z 402 kB → 29 kB (−93%), w 8,5 żądaniach zamiast 49. W katalogu oszczędność wyniosła 78%. Wyszukiwanie prawie się nie zmieniło, bo jego osiem linków z `prefetch={true}` nadal pobiera własne wyniki.

**Partial Prefetching pobiera ułamek bajtów na stronach z wieloma linkami** (Skompresowane bajty RSC pobrane przed kliknięciem · komputer, bez ograniczeń, późne kliknięcie · mediany z 20 przebiegów)

| Pozycja | Katalog → produkt | Dokumentacja → dokumentacja | Wyszukiwanie → wyszukiwanie |
| --- | --- | --- | --- |
| L0 · Klasyczny router | 30 kB | 408 kB | 98 kB |
| C0 · Cache Components | 160 kB | 402 kB | 163 kB |
| P0 · Partial Prefetching | 34 kB | 29 kB | 107 kB |
| P1 · Bez inliningu | 33 kB | 32 kB | 105 kB |
| P2 · Mały inlining | 32 kB | 28 kB | 103 kB |
| P3 · Duży inlining | 34 kB | 33 kB | 105 kB |

Source: Benchmark Outofplace, Next.js 16.3.6. Chart: https://www.outofplace.space/data/blog/nextjs-prefetch-benchmark/charts/pl/chart-partial-prefetching-pobiera-ulamek-bajtow-na-stronach-z-wieloma.png Data: https://www.outofplace.space/data/blog/nextjs-partial-prefetching-benchmark/2026-09-23/summary_by_variant.csv

Jeden wiersz zasługuje na drugie spojrzenie. Włączenie Cache Components bez Partial Prefetching sprawiło, że katalog pobierał pięć razy więcej niż klasyczny router, 30 kB → 160 kB (+426%): prerenderowane szkielety produktów da się wtedy pobrać osobno dla każdego linku. Jeśli włączasz Cache Components na stronach z wieloma linkami, od razu zdecyduj o Partial Prefetching.

## Czy nawigacja jest naprawdę natychmiastowa?

Szkielet tak. Przy Partial Prefetching pojawiał się 43 ms–45 ms po kliknięciu na zwolnionym telefonie. Z tym, po co ktoś kliknął, czyli produktem albo artykułem, jest inaczej: potrzebuje żądania po kliknięciu.

**Samo Cache Components pokazuje treść od razu, Partial Prefetching po jednym żądaniu** (Od kliknięcia do pierwszej klatki z treścią zależną od adresu · telefon, ograniczenie Lighthouse mobile, późne kliknięcie · ms)

| Pozycja | L0 · Klasyczny router | C0 · Cache Components | P0 · Partial Prefetching |
| --- | --- | --- | --- |
| Katalog → produkt | 375 ms | 60 ms | 262 ms |
| Dokumentacja → dokumentacja | 47 ms | 52 ms | 345 ms |
| Wyszukiwanie → wyszukiwanie | 40 ms | 41 ms | 43 ms |

Source: Benchmark Outofplace, Next.js 16.3.6. Chart: https://www.outofplace.space/data/blog/nextjs-prefetch-benchmark/charts/pl/chart-samo-cache-components-pokazuje-tresc-od-razu-partial-prefetching.png Data: https://www.outofplace.space/data/blog/nextjs-partial-prefetching-benchmark/2026-09-23/summary_by_variant.csv

W dokumentacji samo Cache Components pokazało nowy artykuł po 52 ms, Partial Prefetching po 345 ms. Na stronie produktu różnica była mniejsza, 60 ms → 262 ms (+337%), a najwolniejszy był klasyczny router, bo dla dynamicznej trasy nie miał nic wyrenderowanego z góry. Wyszukiwanie, gdzie każdy link ma `prefetch={true}`, pokazywało treść po 40 ms–43 ms w każdej konfiguracji.

### Szkielet, który zobaczysz: 300 ms

Na stronie produktu trzy etapy przychodzą po kolei: szkielet, produkt, a potem część renderowana w chwili żądania.

**Przy Partial Prefetching strona przychodzi w trzech krokach** (Katalog → produkt · telefon, zwolniony, późne kliknięcie · od kliknięcia do każdego etapu na ekranie · ms)

| Pozycja | Szkielet | Treść | Część z serwera |
| --- | --- | --- | --- |
| L0 · Klasyczny router | 375 ms | 375 ms | 375 ms |
| C0 · Cache Components | 60 ms | 60 ms | 342 ms |
| P0 · Partial Prefetching | 47 ms | 262 ms | 562 ms |

Source: Benchmark Outofplace, Next.js 16.3.6. Chart: https://www.outofplace.space/data/blog/nextjs-prefetch-benchmark/charts/pl/chart-przy-partial-prefetching-strona-przychodzi-w-trzech-krokach.png Data: https://www.outofplace.space/data/blog/nextjs-partial-prefetching-benchmark/2026-09-23/summary_by_variant.csv

W dokumentacji szkielet był widoczny przez 298 ms w każdym buildzie z Partial Prefetching. To nie jest czas sieci: odpowiedź dochodziła po 250 ms.

React dołączony do Next.js 16.3.6 (`19.3.0-canary`) [czeka 300 ms](https://github.com/react/react/blob/d083ec1da1e5252abd3ddfdde6dfbc09701a2c51/packages/react-reconciler/src/ReactFiberWorkLoop.js#L528) od ostatniej zamiany między fallbackiem [Suspense](https://react.dev/reference/react/Suspense) a treścią, zanim zatwierdzi kolejną, żeby stan ładowania nie mignął. Efekt uboczny: etapy się łańcuchują. Szkielet, który się pojawi, zostaje na ekranie około 300 ms, nawet gdy dane już są, a osobna granica dla danych z serwera odsłania się 300 ms po poprzedniej. Projektuj szkielety tak, żeby dało się na nie patrzeć, i rozważ jedną granicę tam, gdzie dwie tylko by się łańcuchowały.

### Wczesne kliknięcie

Kliknięcie 50 ms po hydratacji, zanim skończy się jakikolwiek prefetch, zabiera przewagę wszystkim, którzy pobierają z wyprzedzeniem. Samo Cache Components jest wtedy mniej więcej tak samo wolne jak Partial Prefetching, bo obie konfiguracje najpierw pokazują fallback, a potem odczekują 300 ms.

**Przy wczesnym kliknięciu żadna konfiguracja niczego jeszcze nie pobrała** (Kliknięcie 50 ms po hydratacji · telefon, zwolniony · od kliknięcia do treści · ms)

| Pozycja | L0 · Klasyczny router | C0 · Cache Components | P0 · Partial Prefetching |
| --- | --- | --- | --- |
| Katalog → produkt | 367 ms | 374 ms | 389 ms |
| Dokumentacja → dokumentacja | 262 ms | 528 ms | 529 ms |
| Wyszukiwanie → wyszukiwanie | 234 ms | 404 ms | 339 ms |

Source: Benchmark Outofplace, Next.js 16.3.6. Chart: https://www.outofplace.space/data/blog/nextjs-prefetch-benchmark/charts/pl/chart-przy-wczesnym-kliknieciu-zadna-konfiguracja-niczego-jeszcze-nie.png Data: https://www.outofplace.space/data/blog/nextjs-partial-prefetching-benchmark/2026-09-23/summary_by_variant.csv

Ostatni etap, czyli część z serwera, dzieli te kliknięcia na dwie grupy. Przy Partial Prefetching w 6 z 12 wczesnych kliknięć na stronie produktu cała strona była na ekranie po 375 ms–400 ms. W pozostałych 6 dopiero po 650 ms–700 ms, a pomiędzy nie trafił żaden przebieg. Ta przerwa to mniej więcej 300 ms, które React odczekuje między dwoma odsłonięciami: ostatni etap pojawia się razem z produktem albo jedno odczekanie później.

**Ostatni etap przychodzi razem z produktem albo mniej więcej 300 ms później** (Katalog → produkt, wczesne kliknięcie · Partial Prefetching · telefon, zwolniony, przebieg HTTP/2 · od kliknięcia do ostatniego etapu na ekranie · ms)

| Zakres | przebiegi |
| --- | --- |
| 375 ms–400 ms | 6 |
| 400 ms–425 ms | 0 |
| 425 ms–450 ms | 0 |
| 450 ms–475 ms | 0 |
| 475 ms–500 ms | 0 |
| 500 ms–525 ms | 0 |
| 525 ms–550 ms | 0 |
| 550 ms–575 ms | 0 |
| 575 ms–600 ms | 0 |
| 600 ms–625 ms | 0 |
| 625 ms–650 ms | 0 |
| 650 ms–675 ms | 3 |
| 675 ms–700 ms | 3 |

Source: Benchmark Outofplace, Next.js 16.3.6. Chart: https://www.outofplace.space/data/blog/nextjs-prefetch-benchmark/charts/pl/chart-ostatni-etap-przychodzi-razem-z-produktem-albo-mniej-wiecej-300.png Data: https://www.outofplace.space/data/blog/nextjs-partial-prefetching-benchmark/2026-09-23/runs.csv

## Cały rachunek

Partial Prefetching przenosi bajty sprzed kliknięcia na po kliknięciu, a na stronie produktu dodatkowo je podwaja: odpowiedź nawigacji niosła dwie pełne kopie payloadu produktu, 13 kB → 26 kB (+99%) po kompresji. Licząc wszystko, co pobiera nawigacja, razem z prefetchem, w dokumentacji i tak wychodzi daleko do przodu.

**Cała nawigacja w dokumentacji kosztuje mniej więcej jedną dziesiątą bajtów** (Wszystkie skompresowane bajty RSC jednej nawigacji w dokumentacji, prefetch i nawigacja · telefon, zwolniony, późne kliknięcie)

| Pozycja | Wartość | n |
| --- | --- | --- |
| L0 · Klasyczny router | 463 kB | 20 |
| C0 · Cache Components | 458 kB | 20 |
| P0 · Partial Prefetching | 40 kB | 20 |
| P1 · Bez inliningu | 43 kB | 20 |
| P2 · Mały inlining | 39 kB | 20 |
| P3 · Duży inlining | 44 kB | 20 |

Source: Benchmark Outofplace, Next.js 16.3.6. Chart: https://www.outofplace.space/data/blog/nextjs-prefetch-benchmark/charts/pl/chart-cala-nawigacja-w-dokumentacji-kosztuje-mniej-wiecej-jedna-dziesi.png Data: https://www.outofplace.space/data/blog/nextjs-partial-prefetching-benchmark/2026-09-23/summary_by_variant.csv

Mniej więcej 11 razy mniej bajtów w dokumentacji i 2,2 raza mniej w katalogu. Hydratacja i opóźnienie reakcji na stronie startowej nie zmieniły się w żadnej konfiguracji.

## Kiedy `prefetch={true}` się opłaca

[` `](https://nextjs.org/docs/app/api-reference/components/link#prefetch) pobiera też dane z adresu strony docelowej, więc kliknięcie jest tak samo natychmiastowe jak przy pełnym prefetchu. Płacisz za każdy widoczny link: osiem linków wyszukiwania kosztowało 107 kB, więcej niż cała strona dokumentacji przy Partial Prefetching. Używaj go tam, gdzie kliknięcie jest prawdopodobne, a dane da się cache'ować: podpowiedzi wyszukiwania, link „dalej”, kilka pierwszych wyników. Na siatkach kart rób prefetch na zamiar (najechanie albo dotknięcie), zamiast oznaczać każdą kartę.

## prefetchInlining: zostaw włączony

[`experimental.prefetchInlining`](https://nextjs.org/docs/app/api-reference/config/next-config-js/prefetchInlining) łączy małe odpowiedzi prefetchu w jedną. Ustawienie domyślne i nasze dwa zestawy progów różniły się najwyżej jednym żądaniem i kilkoma kilobajtami, bez mierzalnej różnicy w czasie.

Wyłączenie inliningu dodało pięć albo sześć żądań w katalogu i wyszukiwaniu, a przez HTTP/1.1 kosztowało to wczesne kliknięcie w katalogu: sześć połączeń na host się zapycha. Przez [HTTP/2](https://www.rfc-editor.org/rfc/rfc9113.html) kara znikała. Wszystkie inne konfiguracje wypadły na obu protokołach tak samo.

**HTTP/2 ma znaczenie tylko przy wyłączonym inliningu** (Katalog → produkt, wczesne kliknięcie · telefon, zwolniony · od kliknięcia do treści · HTTP/1.1 i HTTP/2 na przemian, po 12 przebiegów · ms)

| Pozycja | HTTP/1.1 | HTTP/2 |
| --- | --- | --- |
| L0 · Klasyczny router | 365 ms | 363 ms |
| C0 · Cache Components | 375 ms | 373 ms |
| P0 · Partial Prefetching | 387 ms | 386 ms |
| P1 · Bez inliningu | 440 ms | 372 ms |
| P2 · Mały inlining | 390 ms | 383 ms |
| P3 · Duży inlining | 387 ms | 386 ms |

Source: Benchmark Outofplace, Next.js 16.3.6. Chart: https://www.outofplace.space/data/blog/nextjs-prefetch-benchmark/charts/pl/chart-http-2-ma-znaczenie-tylko-przy-wylaczonym-inliningu.png Data: https://www.outofplace.space/data/blog/nextjs-partial-prefetching-benchmark/2026-09-23/summary_by_variant.csv

## Którą konfigurację wybrać

```ts title="next.config.ts"
const nextConfig: NextConfig = {
  cacheComponents: true,
  // Jeden App Shell na trasę zamiast prefetchu na każdy link.
  partialPrefetching: true,
};
```

```tsx title="Linki, przy których czekanie by bolało"

  {suggestion}

```

Najwięcej zyskują strony z wieloma linkami do tej samej dynamicznej trasy: dokumentacja, siatki, feedy. Strona z kilkoma linkami do różnych stron zyskuje niewiele.

Tam, gdzie natychmiastowa treść jest ważniejsza niż bajty, a strony są statyczne, zostaw pełny prefetch; gdzie indziej włącz Partial Prefetching. [Konfiguracja segmentu `export const prefetch = 'partial'`](https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/prefetch) pozwala przechodzić trasa po trasie.

Dodaj `prefetch={true}` tam, gdzie następne kliknięcie da się przewidzieć, a dane da się cache'ować.

Na wolnym łączu będzie widoczny przez około 300 ms. Tytuł i nawigację umieść w części niezależnej od adresu: pojawia się niemal od razu.

Ta strona działa na Partial Prefetching: większość naszych stron ma mało linków, a te, które mają ich dużo (blog, case studies), są statyczne i lekkie. Nasze własne produkty, Portivo, Sprawna Matura i Levera, też stoją na Next.js.

## Jak mierzyliśmy

Wygenerowaną aplikację App Router (katalog 48 produktów, 100 stron dokumentacji z menu w layoucie, strona wyszukiwania) zbudowaliśmy sześć razy na Next.js 16.3.6, zmieniając tylko `cacheComponents`, `partialPrefetching` i `experimental.prefetchInlining`, i serwowaliśmy przez `next start`. Headless Chrome otwierał stronę startową w świeżym kontekście przeglądarki, czekał i klikał link, zapisując każde żądanie przez [protokół DevTools](https://chromedevtools.github.io/devtools-protocol/) i mierząc znaczniki od zdarzenia kliknięcia do następnej klatki animacji. Każda kombinacja konfiguracji, scenariusza, telefonu albo komputera, szybkiej albo [zwolnionej sieci (150 ms, 1,6 Mb/s, CPU 4×)](https://github.com/GoogleChrome/lighthouse/blob/main/docs/throttling.md) i wczesnego albo późnego kliknięcia przeszła 20 razy w losowej kolejności, razem 2880 przebiegów; test HTTP/2 dodał 864 kolejne. Mediany mają 95-procentowe przedziały bootstrap ze stałym ziarnem.

To laboratorium: jedna maszyna, localhost, bez CDN, bez obrazów i fontów, wygenerowany tekst i sztuczne 300 ms opóźnienia po stronie serwera. Ograniczanie działa per żądanie, nie na poziomie pakietów, więc czasy bezwzględne są optymistyczne; liczą się porównania. Późne kliknięcie to najlepszy przypadek dla prefetchu, a wczesne to jeden konkretny wyścig; prawdziwi użytkownicy klikają wszędzie pomiędzy. Mechanika App Shella, podwojony payload produktu i 300 ms odsłonięcia dotyczą Next.js 16.3.6 z jego wersją canary Reacta i mogą się zmienić w wydaniu poprawkowym.

Wspólny szkielet jest pobierany przez adres tego linku, który router zaplanuje jako pierwszy, a ta odpowiedź niesie całą stronę dla tego adresu. Kliknięcie dokładnie w ten link nie potrzebuje żadnego żądania. Cele kliknięć wybraliśmy niżej na stronie, żeby to nie podbijało wyników Partial Prefetching.

## Dalsza lektura

- [Next.js: Adopting Partial Prefetching](https://nextjs.org/docs/app/guides/adopting-partial-prefetching): Jak działa App Shell i jak przenosić trasy jedna po drugiej.

- [Next.js: Optimizing prefetching](https://nextjs.org/docs/app/guides/optimizing-prefetching): Prefetch per link przez prefetch={true} i kiedy go używać.

- [Next.js: prefetchInlining](https://nextjs.org/docs/app/api-reference/config/next-config-js/prefetchInlining): Co łączy inlining i dlaczego domyślne ustawienie pasuje większości aplikacji.
