Next.js 16 use cache: Sprievodca kešovaním komponentov

Kompletný sprievodca direktívou use cache v Next.js 16 s praktickými príkladmi. Kešujte komponenty, stránky a funkcie pomocou cacheLife, cacheTag a PPR.

Prečo Next.js 16 zmenil celý prístup ku kešovaniu

Ak ste niekedy pracovali s Next.js App Routerom pred verziou 16, viete presne, o čom hovorím. Kešovanie bolo jednoducho chaos. Framework kešoval takmer všetko automaticky — fetch požiadavky, celé stránky, dokonca aj dynamické routes. A vývojári sa neustále pýtali to isté: „Prečo sa mi zobrazujú staré dáta?" alebo „Ako prinútim Next.js, aby nekešoval túto stránku?"

Úprimne, bolo to frustrujúce.

Next.js 16 tento problém rieši radikálne — prešiel z modelu „kešuj všetko automaticky" na model „kešuj len to, čo explicitne označíš". A kľúčom k tomuto novému prístupu je direktíva use cache. Keď hovorím kľúčom, myslím to doslova. Bez nej sa proste nič nekešuje.

V tomto sprievodcovi si prejdeme naozaj všetko — od základného nastavenia, cez praktické príklady s cacheLife a cacheTag, až po migráciu z unstable_cache a integráciu s Partial Prerendering (PPR). Tak poďme na to.

Čo je vlastne direktíva use cache

Direktíva "use cache" je nový jazykový primitív v Next.js 16. Funguje podobne ako "use client" a "use server" — umiestnite ju na začiatok funkcie, komponentu alebo celého súboru a Next.js kompilátor sa postará o to, aby návratová hodnota bola kešovaná.

Čo je na tom naozaj super: na rozdiel od predchádzajúceho unstable_cache, ktorý vedel kešovať iba JSON dáta, use cache dokáže kešovať čokoľvek, čo React Server Components vedia serializovať. Vrátane JSX komponentov a celých stránok. To je dosť veľký skok vpred.

Dva spôsoby použitia

  • Na úrovni dát — kešovanie funkcie, ktorá načítava dáta z databázy alebo API
  • Na úrovni UI — kešovanie celého komponentu alebo stránky vrátane vyrenderovaného HTML

Aktivácia Cache Components v next.config.ts

Pred použitím use cache musíte aktivovať Cache Components vo vašej konfigurácii. Dobrá správa — v Next.js 16 je to len jeden príznak:

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

const nextConfig: NextConfig = {
  cacheComponents: true,
}

export default nextConfig

Tento jediný príznak odomkne celý nový kešovací systém vrátane Partial Prerendering (PPR). Nahrádza predchádzajúce experimentálne príznaky ako experimental.ppr a experimental.dynamicIO, čo je príjemné zjednodušenie.

Základné použitie use cache

Kešovanie celej stránky

Najjednoduchšie použitie je označiť celú stránku ako kešovanú. V praxi to vyzerá takto:

// app/blog/page.tsx
import { cacheLife } from 'next/cache'

export default async function BlogPage() {
  'use cache'
  cacheLife('hours')

  const posts = await fetch('https://api.example.com/posts')
  const data = await posts.json()

  return (
    <main>
      <h1>Blog</h1>
      {data.map((post: any) => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.excerpt}</p>
        </article>
      ))}
    </main>
  )
}

Kešovanie dátovej funkcie

V reálnych projektoch ale častejšie budete chcieť kešovať iba dátovú vrstvu — samotnú funkciu, ktorá načítava dáta. Toto je podľa mňa najužitočnejší vzor:

// lib/data.ts
import { cacheLife, cacheTag } from 'next/cache'

export async function getProducts(categoryId: string) {
  'use cache'
  cacheLife('hours')
  cacheTag('products', `category-${categoryId}`)

  const res = await fetch(`https://api.example.com/products?category=${categoryId}`)
  return res.json()
}

Všimnite si, že argument categoryId sa automaticky stáva súčasťou kľúča keše. Rôzne kategórie vytvárajú rôzne záznamy v keši — bez akejkoľvek manuálnej konfigurácie. Proste to funguje.

Kešovanie na úrovni súboru

A potom je tu ešte tretia možnosť. Ak chcete kešovať všetky exportované funkcie v súbore, umiestnite direktívu na jeho úplný začiatok:

'use cache'

import { cacheLife } from 'next/cache'

export async function getUser(id: string) {
  cacheLife('minutes')
  const res = await fetch(`/api/users/${id}`)
  return res.json()
}

export async function getUserPosts(userId: string) {
  cacheLife('hours')
  const res = await fetch(`/api/users/${userId}/posts`)
  return res.json()
}

Ako sa generujú kľúče keše

Jedna z najväčších výhod use cache oproti unstable_cache je automatické generovanie kľúčov keše. Pamätáte si tie ručne definované polia reťazcov? To je minulosť. Kompilátor to teraz robí za vás.

Kľúč keše sa skladá z:

  • Build ID — unikátne pre každý build, takže nový deploy automaticky invaliduje celú keš
  • Function ID — bezpečný hash lokácie a signatúry funkcie v kóde
  • Serializovateľné argumenty — props (pre komponenty) alebo argumenty funkcie
  • Uzavreté premenné — ak funkcia odkazuje na premenné z vonkajšieho rozsahu, automaticky sa stanú súčasťou kľúča

Takže ak máte funkciu getProduct(id), každé unikátne id vytvorí samostatný záznam v keši. Žiadna mágia, žiadne prekvapenia. Presne tak, ako by to malo fungovať.

Riadenie životnosti keše s cacheLife

Funkcia cacheLife určuje, ako dlho zostanú dáta v keši. Používa sa vždy v kombinácii s use cache — bez nej by keš v podstate nemala definovanú životnosť.

Vstavané profily

Next.js ponúka niekoľko vstavaných profilov, takže nemusíte hneď počítať sekundy:

cacheLife('seconds')  // Kešuje na sekundy
cacheLife('minutes')  // Kešuje na minúty
cacheLife('hours')    // Kešuje na hodiny
cacheLife('days')     // Kešuje na dni
cacheLife('weeks')    // Kešuje na týždne
cacheLife('max')      // Maximálna doba kešovania

Vlastné profily v konfigurácii

Keď vám vstavané profily nestačia (a verťe mi, v reálnom projekte rýchlo nestačia), môžete si definovať vlastné v next.config.ts. Každý profil má tri parametre:

  • stale — ako dlho klient používa kešované dáta bez kontaktovania servera
  • revalidate — ako často sa na serveri generuje čerstvá verzia
  • expire — maximálna doba, po ktorej sa záznam z keše úplne odstráni
// next.config.ts
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  cacheComponents: true,
  cacheLife: {
    produkty: {
      stale: 3600,      // 1 hodina
      revalidate: 900,  // 15 minút
      expire: 86400,    // 1 deň
    },
    blogPrispevky: {
      stale: 86400,       // 1 deň
      revalidate: 3600,   // 1 hodina
      expire: 604800,     // 7 dní
    },
  },
}

export default nextConfig

Potom ich jednoducho použijete podľa názvu:

export async function getProducts() {
  'use cache'
  cacheLife('produkty')
  // ...
}

Podmienená životnosť keše

Toto je jeden z mojich obľúbených vzorov — rôzne doby kešovania podľa toho, aký výsledok dostanete:

import { cacheLife, cacheTag } from 'next/cache'

async function getArticle(slug: string) {
  'use cache'
  cacheTag(`article-${slug}`)

  const article = await fetchArticle(slug)

  if (!article) {
    cacheLife('minutes') // Krátka keš pre neexistujúci článok
    return null
  }

  cacheLife('days') // Dlhá keš pre publikovaný článok
  return article
}

Prečo je to užitočné? Predstavte si, že niekto zadá neexistujúci slug. Nechcete mať odpoveď „nenájdené" kešovanú celý deň — ale pár minút je v poriadku.

Invalidácia keše s cacheTag a revalidateTag

Funkcia cacheTag umožňuje označiť záznamy v keši tagmi, ktoré potom môžete cielene invalidovať pomocou revalidateTag. Je to vlastne dosť intuitívne, keď to raz pochopíte.

Označovanie dát tagmi

import { cacheLife, cacheTag } from 'next/cache'

export async function getProduct(id: string) {
  'use cache'
  cacheLife('produkty')
  cacheTag('products', `product-${id}`)

  const res = await fetch(`https://api.example.com/products/${id}`)
  return res.json()
}

Invalidácia zo Server Action

// app/actions/product.ts
'use server'

import { revalidateTag } from 'next/cache'

export async function updateProduct(id: string, data: FormData) {
  await db.products.update({
    where: { id },
    data: { name: data.get('name') as string },
  })

  // Invalidácia konkrétneho produktu
  revalidateTag(`product-${id}`)

  // Invalidácia celého zoznamu produktov
  revalidateTag('products')
}

updateTag pre okamžitú aktualizáciu

Next.js 16 prináša aj nové API updateTag, ktoré rieši jeden konkrétny problém: potrebujete vzor „prečítaj si vlastné zápisy" (read-your-own-writes). Na rozdiel od revalidateTag, ktorý funguje na princípe stale-while-revalidate, updateTag zaistí okamžitú invalidáciu:

'use server'

import { updateTag } from 'next/cache'

export async function addToCart(productId: string) {
  await db.cart.add({ productId })
  updateTag('cart') // Používateľ okamžite vidí aktualizovaný košík
}

Toto je presne ten scenár, kde revalidateTag nestačí. Keď používateľ pridá produkt do košíka, chce vidieť zmenu hneď — nie po revalidácii na pozadí.

Tri varianty direktívy use cache

Tu to začína byť zaujímavé. Next.js 16 ponúka tri varianty kešovacej direktívy, každú pre úplne iný scenár:

use cache (štandardná)

Kešuje na serveri pomocou in-memory LRU úložiska. Pre väčšinu prípadov je to presne to, čo potrebujete — dáta zdieľané medzi všetkými používateľmi.

async function getGlobalStats() {
  'use cache'
  cacheLife('hours')
  return await fetchStats()
}

use cache: remote

Kešuje do externého úložiska ako Redis alebo KV databáza. Použite vtedy, keď potrebujete trvalú keš zdieľanú medzi všetkými inštanciami servera:

async function getPopularProducts() {
  'use cache: remote'
  cacheLife('hours')
  return await fetchPopularProducts()
}

use cache: private

A potom je tu private varianta — kešuje iba v pamäti prehliadača. Veľká výhoda? Umožňuje prístup k runtime API ako cookies() a headers(), čo štandardná varianta neumožňuje:

async function getUserDashboard() {
  'use cache: private'
  cacheLife('minutes')
  const session = await cookies()
  const userId = session.get('userId')?.value
  return await fetchDashboardData(userId)
}

Partial Prerendering — kešovanie v praxi

Cache Components a PPR (Partial Prerendering) idú ruka v ruke. Keď aktivujete cacheComponents, Next.js automaticky využíva PPR — predrenderuje statický shell stránky a dynamický obsah streamuje paralelne. V praxi to znamená oveľa rýchlejší prvý render.

Ako to funguje

Predstavte si typickú e-shop stránku. Informácie o produkte sa menia zriedka (ideálne na kešovanie), ale recenzie pribúdajú neustále (chceme čerstvé dáta):

// app/product/[id]/page.tsx
import { Suspense } from 'react'

export default async function ProductPage({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  const { id } = await params

  return (
    <main>
      {/* Kešovaný obsah — predrenderovaný do statického shellu */}
      <ProductInfo id={id} />

      {/* Dynamický obsah — streamovaný za behu */}
      <Suspense fallback={<p>Načítavam recenzie...</p>}>
        <ProductReviews id={id} />
      </Suspense>

      <Suspense fallback={<p>Načítavam odporúčania...</p>}>
        <RecommendedProducts id={id} />
      </Suspense>
    </main>
  )
}

// Tento komponent je kešovaný
async function ProductInfo({ id }: { id: string }) {
  'use cache'
  cacheLife('hours')
  cacheTag(`product-${id}`)

  const product = await getProduct(id)
  return (
    <section>
      <h1>{product.name}</h1>
      <p>{product.description}</p>
      <span>{product.price} €</span>
    </section>
  )
}

// Tento komponent NIE je kešovaný — streamuje sa za behu
async function ProductReviews({ id }: { id: string }) {
  const reviews = await getReviews(id) // Vždy čerstvé dáta
  return (
    <section>
      <h2>Recenzie</h2>
      {reviews.map((r: any) => (
        <div key={r.id}>{r.text}</div>
      ))}
    </section>
  )
}

Výsledok? Používateľ okamžite vidí informácie o produkte (z keše) a recenzie sa doťahujú asynchrónne. Celá odpoveď ide v jednej HTTP požiadavke — žiadne extra round-tripy. To je ten moment, keď si poviete: „Aha, tak preto to celé prepísali."

Migrácia z unstable_cache na use cache

Ak máte existujúci projekt na unstable_cache, migrácia je (našťastie) pomerne priamočiara. Poďme si porovnať starý a nový prístup vedľa seba.

Pred (unstable_cache)

import { unstable_cache } from 'next/cache'

const getCachedProducts = unstable_cache(
  async (categoryId: string) => {
    const res = await fetch(`/api/products?cat=${categoryId}`)
    return res.json()
  },
  ['products'],           // Manuálne kľúče
  {
    revalidate: 3600,      // TTL v sekundách
    tags: ['products'],   // Tagy pre invalidáciu
  }
)

Po (use cache)

import { cacheLife, cacheTag } from 'next/cache'

async function getProducts(categoryId: string) {
  'use cache'
  cacheLife('hours')
  cacheTag('products')

  const res = await fetch(`/api/products?cat=${categoryId}`)
  return res.json()
}

Vidíte ten rozdiel? Oveľa menej boilerplate kódu. Hlavné rozdiely v skratke:

  • Žiadne manuálne kľúče — kompilátor generuje kľúče automaticky z argumentov
  • Žiadny wrapper — direktíva sa pridáva priamo do funkcie
  • Tagy cez cacheTag — namiesto konfiguračného objektu
  • Životnosť cez cacheLife — namiesto revalidate v konfigurácii

Automatický codemod

Next.js 16 obsahuje codemod, ktorý vám s migráciou pomôže. Automaticky premenuje experimental.dynamicIO na cacheComponents a odstráni prefix unstable_ z importov:

npx @next/codemod@latest upgrade

Ale buďte pripravení — codemod nie je dokonalý. Pri zložitejších transformáciách zanechá komentáre @next-codemod-error, ktoré si budete musieť opraviť ručne. Väčšinu jednoduchých prípadov ale zvládne bez problémov.

Dôležité obmedzenia a úskalia

Skôr než začnete use cache používať všade, je dobré poznať niekoľko obmedzení. Zachránite si tým hodiny debugovania.

Žiadny priamy prístup k runtime API

Toto je asi najčastejšia chyba, na ktorú vývojári narážajú. Kešované funkcie nemajú priamy prístup k cookies(), headers() ani searchParams. Hodnoty z nich musíte prečítať mimo kešovaného rozsahu a odovzdať ich ako argumenty:

// Nesprávne — spôsobí chybu
async function getCachedData() {
  'use cache'
  const session = await cookies() // Chyba!
  return fetchData(session)
}

// Správne — hodnotu odovzdáme ako argument
async function getCachedData(userId: string) {
  'use cache'
  return fetchData(userId)
}

// V stránke alebo layoute:
export default async function Page() {
  const session = await cookies()
  const userId = session.get('userId')?.value
  const data = await getCachedData(userId!)
  return <div>{/* ... */}</div>
}

Iba serializovateľné argumenty

Funkcie s use cache akceptujú iba serializovateľné hodnoty ako argumenty. Objekty, reťazce, čísla a polia fungujú bez problémov. Inštancie tried, funkcie alebo symboly nie — a chybová hláška vám to dá jasne vedieť.

In-memory keš v serverless prostredí

Na toto pozor, ak nasadzujete na serverless platformy. Štandardná use cache ukladá záznamy do pamäte. V serverless prostredí (AWS Lambda, Vercel Edge Functions) sa pamäť medzi inštanciami nezdieľa a zvyčajne sa po obsúžení požiadavky vymaže. Pre takéto prípady jednoznačne zvážte use cache: remote s externým úložiskom ako Redis.

Často kladené otázky

Aký je rozdiel medzi use cache a fetch cache v predchádzajúcich verziách Next.js?

Predchádzajúce verzie automaticky kešovali fetch požiadavky a vývojári museli aktívne kešovanie vypínať. Direktíva use cache v Next.js 16 funguje presne opačne — štandardne sa nič nekešuje a vy explicitne označíte, čo kešovať chcete. A navyše dokáže kešovať nielen dáta, ale aj celé komponenty a stránky.

Môžem použiť use cache s databázovými ORM ako Prisma alebo Drizzle?

Áno, bez problémov. Jednoducho obalíte svoju dátovú funkciu direktívou use cache. Nezáleží na tom, odkiaľ dáta pochádzajú — fetch, Prisma, Drizzle alebo čokoľvek iné. Dôležité je len to, aby argumenty funkcie boli serializovateľné a návratová hodnota takisto.

Nahradí use cache úplne ISR (Incremental Static Regeneration)?

Prakticky áno. Direktíva use cache s cacheLife poskytuje rovnakú funkcionalitu ako ISR — servírovanie statického obsahu s pravidelnou revalidáciou na pozadí. Ale ponúka omnoho jemnejšiu granularitu, pretože môžete kešovať na úrovni jednotlivých komponentov, nie iba celých stránok. Podľa mňa je to jasný nástupca ISR.

Ako funguje use cache s autentifikáciou a personalizovaným obsahom?

Máte dve možnosti. Buď použijete use cache: private, ktorá kešuje iba v prehliadači a má prístup k cookies(). Alebo (a to je bežnejší prístup) prečítate session mimo kešovaného rozsahu, odovzdáte identifikátor používateľa ako argument a use cache vytvorí samostatný záznam pre každého používateľa.

Je use cache stabilná funkcia alebo ešte experimentálna?

V Next.js 16 je use cache plne stabilná, produkčne pripravená funkcia. Aktivuje sa prostredníctvom cacheComponents: true v next.config.ts. Predchádzajúca unstable_cache je oficiálne označená ako legacy a tím Next.js odporúča migráciu na use cache.

O Autorovi Editorial Team

Our team of expert writers and editors.