---
title: "use cache w Next.js: klucz, czas życia i updateTag"
description: "Jak działa use cache w Next.js, na żywym demo: klucz cache tworzą argumenty, cacheLife ustala czas życia wpisu, a updateTag czyści wpisy jednego tagu."
canonical_url: "https://www.outofplace.space/pl/blog/use-cache-w-nextjs-jak-dziala"
language: "pl"
last_updated: "2026-10-19"
alternates:
  en: "https://www.outofplace.space/blog/nextjs-use-cache.md"
---

# use cache w Next.js: klucz, czas życia i updateTag

> Jak działa use cache w Next.js, na żywym demo: klucz cache tworzą argumenty, cacheLife ustala czas życia wpisu, a updateTag czyści wpisy jednego tagu.

Opublikowano 2026-10-19 · Kategoria: Next.js i React · Tagi: Next.js, Cache, React

Wpisz `"use cache"` na początku funkcji asynchronicznej, a Next.js zapisze jej wynik pod kluczem zbudowanym z builda, funkcji i jej argumentów: wywołania z tymi samymi argumentami dzielą jeden wpis, inne argumenty dostają własny. Wpis żyje tak długo, jak pozwala profil [`cacheLife`](https://nextjs.org/docs/app/api-reference/functions/cacheLife) (bez wywołania profil `default` odświeża go po 15 minutach i nigdy nie wygasza po czasie), a Server Action, która wywoła `updateTag` z tagiem wpisu z `cacheTag`, czyści tylko wpisy z tym tagiem. Wszystkie trzy rzeczy pokazujemy na jednym demo na Next.js 16.4.0, a jego test w Playwright sprawdza każde twierdzenie o demo z tego tekstu. We własnej aplikacji używaj 16.4.1 lub nowszej wersji: naprawia krytyczne RCE w funkcjach `use cache`, jedną z [poprawek RCE w React i Next.js z października 2026](https://www.outofplace.space/pl/blog/nowosci-web-dev-2026-10-18).

Demo na żywo: https://lab.outofplace.space/demos/nextjs-use-cache (Next.js 16.4.0; kod z commita 6ad61f5: https://github.com/outof-place/outofplace-lab/tree/6ad61f5c5a9cbe5835d658cf51f001e1126e1d82/app/demos/nextjs-use-cache)

## Jak działa use cache: kiedy dwa wywołania dzielą wpis

[Dokumentacja `use cache`](https://nextjs.org/docs/app/api-reference/directives/use-cache#cache-keys) wymienia, co po serializacji trafia do klucza wpisu:

| Składnik klucza | Co to znaczy w praktyce |
| --- | --- |
| Build ID (albo `deploymentId`) | Nowy deploy zaczyna każdy wpis od nowa. |
| ID funkcji | Skrót położenia i sygnatury funkcji: te same argumenty w innej funkcji z cache to inny wpis. |
| Serializowalne argumenty | Każdy inny argument (albo zestaw propsów) to osobny wpis. |
| Zmienne z domknięcia | Wartości czytane z zewnętrznego zakresu trafiają do klucza jak argumenty. |
| Skrót odświeżenia HMR | Tylko w trybie deweloperskim: edycja kodu unieważnia cache. |

Demo ma jedną funkcję z cache. Tworzy id i znacznik czasu w swoim ciele, więc nowe id oznacza, że ciało wykonało się ponownie:

app/demos/nextjs-use-cache/get-stamp.ts (https://github.com/outof-place/outofplace-lab/blob/6ad61f5c5a9cbe5835d658cf51f001e1126e1d82/app/demos/nextjs-use-cache/get-stamp.ts)

```ts
import { cacheLife, cacheTag } from "next/cache";

export type City = "gdansk" | "krakow";

// One cached function. The entry's key is built from its argument, so getStamp("gdansk") and
// getStamp("krakow") are two entries, and every call with "gdansk" shares one.
export async function getStamp(city: City) {
  "use cache";
  cacheLife("hours"); // revalidate after 1 hour, expire after 1 day
  cacheTag(`city-${city}`); // updateTag("city-gdansk") clears this city's entry only

  // Produced inside the cached body: a new id means the body ran again.
  return { id: crypto.randomUUID().slice(0, 8), at: new Date().toISOString() };
}
```

Strona wywołuje ją trzy razy: dwa razy z `"gdansk"` i raz z `"krakow"`. Oba wiersze Gdańska pokazują to samo id i czas, bo ten sam argument zwraca ten sam wpis. Kraków ma inne id: miasta mają osobne wpisy. Wywołania nie różnią się niczym innym, więc o wpisie decyduje sam argument.

Argumenty muszą być serializowalne: typy proste, zwykłe obiekty, tablice, Date, Map i Set przejdą, instancje klas i obiekty URL nie, a funkcje i JSX można tylko [przekazać dalej](https://nextjs.org/docs/app/api-reference/directives/use-cache#pass-through-non-serializable-arguments) bez zaglądania do nich. Przekazuj id rekordu, a nie obiekt klienta, a klucz zostanie mały i stabilny.

## Jak długo żyje wpis use cache

Przeładuj demo: id i czasy z cache zostają te same, a wiersz „Not cached”, który czeka na żądanie przez `connection()`, zmienia się przy każdym wczytaniu. Z wyłączonym JavaScriptem przeglądarka pokazuje HTML, który serwer wysłał najpierw: id z cache już w nim są, a wiersz bez cache pokazuje tylko placeholder. Zgadza się to z dokumentacją: przy Cache Components [prerender wypełnia wpis](https://nextjs.org/docs/app/api-reference/directives/use-cache#cache-output), a jego wynik staje się częścią statycznej powłoki strony.

Jak długo tam zostaje, ustala profil `cacheLife`. [Wbudowane profile](https://nextjs.org/docs/app/api-reference/functions/cacheLife#preset-cache-profiles) według dokumentacji z 19 października 2026:

| Profil | `stale` (przeglądarka) | `revalidate` (serwer) | `expire` |
| --- | ---: | ---: | ---: |
| `default` (bez `cacheLife`) | 5 minut | 15 minut | nigdy |
| `seconds` | 30 sekund | 1 sekunda | 1 minuta |
| `minutes` | 5 minut | 1 minuta | 1 godzina |
| `hours` (w demo) | 5 minut | 1 godzina | 1 dzień |
| `days` | 5 minut | 1 dzień | 1 tydzień |
| `weeks` | 5 minut | 1 tydzień | 30 dni |
| `max` | 5 minut | 30 dni | 1 rok |

`revalidate` to moment, po którym kolejne żądanie dostaje kopię z cache, a świeża powstaje w tle; `expire` to czas, przez jaki wpis może nie być pobierany, zanim następne żądanie poczeka na świeży render. `stale` dotyczy przeglądarki: router po stronie klienta trzyma wynik tak długo, ale co najmniej 30 sekund. To ten sam cache, do którego trafiają prefetchowane strony; tę część zmierzył nasz [benchmark prefetchu w Next.js](https://www.outofplace.space/pl/blog/nextjs-prefetch-zmierzony).

Krótki czas życia zmienia, gdzie wpis może trafić. Według [progów prerenderu w cacheLife](https://nextjs.org/docs/app/api-reference/functions/cacheLife#prerendering-behavior) `expire` poniżej 5 minut albo `revalidate` równe 0 robi z niego dynamiczną dziurę renderowaną w czasie żądania, więc z gotowych profili tylko `seconds` nigdy nie trafia do statycznej powłoki. Taki komponent owiń w `<Suspense>`.

## Jak wyczyścić wpis use cache przez updateTag

[`updateTag`](https://nextjs.org/docs/app/api-reference/functions/updateTag) można wywołać tylko w Server Action. Od razu wygasza każdy wpis z danym tagiem, a następne żądanie czeka na świeże dane, zamiast dostać starą kopię. Akcja w demo bierze miasto z przycisku i czyści tag tego miasta:

app/demos/nextjs-use-cache/actions.ts (https://github.com/outof-place/outofplace-lab/blob/6ad61f5c5a9cbe5835d658cf51f001e1126e1d82/app/demos/nextjs-use-cache/actions.ts)

```ts
"use server";

import { updateTag } from "next/cache";

import type { City } from "./get-stamp";

const cities: City[] = ["gdansk", "krakow"];

// A Server Action: updateTag works only here. It expires every entry tagged with this city, so
// the next render runs getStamp for that city again and reuses the other city's entry.
export async function clearCity(formData: FormData) {
  const city = formData.get("city");
  if (!cities.includes(city as City)) return;
  updateTag(`city-${city}`);
}
```

Kliknij Clear przy Gdańsku: bez przeładowania Gdańsk dostaje nowe id (w obu wierszach, bo dzielą wpis), a Kraków zachowuje swoje. Zasięg czyszczenia wyznacza jeden tag na miasto: `updateTag` wygasza każdy wpis z tym tagiem, więc wspólny tag obu miast wyczyściłby też Kraków.

Sama strona to zwykłe Server Components: wiersze z cache, wiersz bez cache w `<Suspense>` i formularz dla każdego miasta, który wysyła akcję:

app/demos/nextjs-use-cache/page.tsx (https://github.com/outof-place/outofplace-lab/blob/6ad61f5c5a9cbe5835d658cf51f001e1126e1d82/app/demos/nextjs-use-cache/page.tsx)

```tsx
import { connection } from "next/server";
import { Suspense } from "react";

import { clearCity } from "./actions";
import { getStamp, type City } from "./get-stamp";
import styles from "./stamps.module.css";

export default function Page() {
  return (
    <>
      <div className={styles.rows}>
        <CachedRow city="gdansk" name="Gdansk" clearable />
        <CachedRow city="gdansk" name="Gdansk again" />
        <CachedRow city="krakow" name="Krakow" clearable />
        <Suspense fallback={<Row name="Not cached" call="connection()" pending />}>
          <UncachedRow />
        </Suspense>
      </div>
      <p className={styles.reload}>
        <a href="/demos/nextjs-use-cache">Reload</a>, then clear one city.
      </p>
    </>
  );
}

// Same function, same argument, same entry: both Gdansk rows show one id.
async function CachedRow({ city, name, clearable }: { city: City; name: string; clearable?: boolean }) {
  const stamp = await getStamp(city);
  return <Row name={name} call={`getStamp("${city}")`} stamp={stamp} clear={clearable ? city : undefined} />;
}

// No cache: connection() waits for a request, so this runs on every load.
async function UncachedRow() {
  await connection();
  const stamp = { id: crypto.randomUUID().slice(0, 8), at: new Date().toISOString() };
  return <Row name="Not cached" call="connection()" stamp={stamp} />;
}

type RowProps = { name: string; call: string; stamp?: { id: string; at: string }; clear?: City; pending?: boolean };

function Row({ name, call, stamp, clear, pending }: RowProps) {
  return (
    <section className={styles.row} aria-label={name}>
      <div>
        <h2>{name}</h2>
        <code>{call}</code>
      </div>
      <p className={styles.stamp}>
        {stamp ? (
          <>
            <output>{stamp.id}</output>
            <time dateTime={stamp.at}>{stamp.at.slice(11, 19)} UTC</time>
          </>
        ) : (
          pending && <span>rendering…</span>
        )}
      </p>
      {clear && (
        <form action={clearCity}>
          <button name="city" value={clear}>
            Clear
          </button>
        </form>
      )}
    </section>
  );
}
```

W webhooku albo Route Handlerze, gdzie `updateTag` rzuca błąd, użyj [`revalidateTag(tag, "max")`](https://nextjs.org/docs/app/api-reference/functions/revalidateTag): serwuje kopię z cache, a świeża powstaje w tle.

## Jak dodać use cache do własnej aplikacji Next.js

**1. Włącz Cache Components.**
Ustaw `cacheComponents: true` w `next.config.ts`. `use cache` należy do Cache Components i działa tylko z tą opcją.

**2. Cache'uj funkcję, nie żądanie.**
Wpisz `"use cache"` w pierwszej linii funkcji asynchronicznej, która ładuje wspólne dane. Cookies, nagłówki i parametry wyszukiwania czytaj poza nią i przekazuj potrzebne wartości jako argumenty.

**3. Ustaw cacheLife wprost.**
Wywołaj `cacheLife("hours")` (albo profil pasujący do tego, jak często zmieniają się dane) w każdej funkcji z cache, żeby czas życia był widoczny tam, gdzie ją zdefiniowano, zamiast zależeć od `default`.

**4. Otaguj wpis tym, co go zmienia.**
Wywołaj [`cacheTag`](https://nextjs.org/docs/app/api-reference/functions/cacheTag) z jednym tagiem na rekord, a gdy rekord pokazuje też lista, dodaj drugi tag dla listy: jeden wpis może mieć kilka tagów.

**5. Czyść tag tam, gdzie zmieniają się dane.**
Po zapisie w Server Action wywołaj `updateTag` z tagiem rekordu. Z webhooka albo Route Handlera wywołaj `revalidateTag(tag, "max")`.

**6. Sprawdź to na buildzie produkcyjnym.**
Uruchom `next build` i `next start`, a potem przeładuj stronę i wyczyść tag jak w demo. W trybie deweloperskim skrót HMR jest częścią klucza, więc każda edycja czyści cache.

Konfiguracja to jedna linia:

```ts title="next.config.ts"
const nextConfig = { cacheComponents: true };
```

## Co psuje use cache

[GHSA-673q-mjm6-r5xr](https://github.com/vercel/next.js/security/advisories/GHSA-673q-mjm6-r5xr), opublikowany 14 października 2026, to krytyczne zdalne wykonanie kodu w funkcjach `use cache`, które zwracają kilka tablic typowanych, w Next.js od 16.0.0 do 16.4.0. Obejścia nie ma, a komunikat zaleca aktualizację do 16.4.1, nawet jeśli żadna twoja funkcja nie zwraca tablic typowanych.

### cookies() i headers() w funkcji z cache

Funkcja z cache nie może czytać `cookies()`, `headers()` ani `searchParams`, a zakaz obejmuje cały stos wywołań: helper, który czyta jedną z tych wartości, kończy się tym samym błędem [`next-request-in-use-cache`](https://nextjs.org/docs/app/api-reference/directives/use-cache#request-time-apis). Na trasie renderowanej dynamicznie błąd wychodzi dopiero przy jej uruchomieniu, więc build może przejść, a `next start` się wyłożyć. Odczytaj wartość w komponencie i przekaż ją jako argument: wtedy trafi też do klucza.

### Gdzie pasuje use cache: private

Gdy wyniesienie odczytu żądania na zewnątrz jest niepraktyczne, [`'use cache: private'`](https://nextjs.org/docs/app/api-reference/directives/use-cache-private) pozwala na `cookies()`, `headers()` i `searchParams` w zakresie z cache. To inny cache: na produkcji pasujące wywołania w obrębie jednego żądania dzielą wynik, ale nic nie zostaje zapisane na serwerze między żądaniami, a funkcja działa w czasie żądania, poza statyczną powłoką. Przeglądarka może trzymać wynik przez czas `stale`. `connection()` jest zakazane w obu dyrektywach.

### Hosting serverless i nowe deploye

Domyślny handler trzyma wpisy w pamięci. Na serverless dokumentacja mówi, że [wpisy zwykle nie przetrwają między żądaniami](https://nextjs.org/docs/app/api-reference/directives/use-cache#runtime-caching-considerations), bo każde może trafić na inną instancję, a cache z czasu builda działa normalnie. Na własnym serwerze jeden proces je trzyma, a limit ustawia `cacheMaxMemorySize`. Jeśli na serverless liczą się wpisy tworzone w czasie żądania, dokumentacja wskazuje `'use cache: remote'` i handler wspólny dla instancji. A że build ID jest w kluczu, każdy deploy zaczyna z pustym cache.

### Brak cacheLife w zagnieżdżonym cache

Funkcja z `use cache` bez własnego `cacheLife` dostaje profil `default`, a wewnętrzny cache z krótszym czasem życia może go skrócić, jak opisują [zasady zagnieżdżania w cacheLife](https://nextjs.org/docs/app/api-reference/functions/cacheLife#nested-caching-behavior). Zagnieżdżenie cache `seconds` w zewnętrznym bez `cacheLife` kończy build błędem podczas prerenderu. Jawne `cacheLife` w każdej funkcji z cache zapobiega obu sytuacjom.

## Dalsza lektura

- [Next.js: use cache](https://nextjs.org/docs/app/api-reference/directives/use-cache): Klucze cache, serializacja, cache w czasie działania zależnie od hostingu i rewalidacja.

- [Next.js: cacheLife](https://nextjs.org/docs/app/api-reference/functions/cacheLife): Gotowe profile z czasami stale, revalidate i expire oraz progi prerenderu.

- [Next.js: updateTag](https://nextjs.org/docs/app/api-reference/functions/updateTag): Czyszczenie tagu z Server Action i różnice względem revalidateTag.

- [Next.js: use cache: private](https://nextjs.org/docs/app/api-reference/directives/use-cache-private): Cache funkcji, które czytają cookies lub nagłówki, i to, czego nie zapisuje.

- [Next.js: Remote Code Execution in use cache functions](https://github.com/vercel/next.js/security/advisories/GHSA-673q-mjm6-r5xr): Komunikat z 14 października 2026: wersje od 16.0.0 do 16.4.0, poprawka w 16.4.1.

- [Next.js prefetch zmierzony: Partial Prefetching w 16.3](https://www.outofplace.space/pl/blog/nextjs-prefetch-zmierzony): 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.
