--- name: cache-components description: "Expert guidance for Next.js Cache Components and Partial Prerendering (PPR). Use when implementing 'use cache' directive, configuring cache lifetimes with cacheLife(), tagging cached data with cacheTag(), invalidating caches with updateTag()/revalidateTag(), optimizing static vs dynamic content boundaries, instant navigation validation, 'use cache: private', pass-through/interleaving patterns, GET Route Handler caching, debugging cache issues, and reviewing Cache Component implementations." metadata: version: "1.0" --- # Cache Components Keep the project's caching mode unless migration is requested. Confirm the installed Next.js version and `cacheComponents`/`partialPrefetching` configuration before applying these patterns. Read the relevant guide in `node_modules/next/dist/docs/` first (bundled since 16.2; in a monorepo, use the app's own `next` package). Web paths map to files with numbered folders, so find them by name: `.../functions/cacheLife` is `01-app/03-api-reference/04-functions/cacheLife.md`. Under `01-app/`, start with `02-guides/migrating-to-cache-components.md`, `01-getting-started/08-caching.md` and `02-guides/instant-navigation.md`. Error pages under `/docs/messages` are not bundled; `next dev`/`next build` print the fix options inline. Without local docs, use the [official caching docs](https://nextjs.org/docs/app/getting-started/caching). Exact exports and signatures are also in `node_modules/next/cache.d.ts`. For static-output and staged-deferral questions in 16.4+, find the installed `ensureStatic` route-segment reference and `navigation`/`prefetch` function references by name. The goal is a useful prerendered shell with freshness and authorization rules that remain correct after mutations, deploys and client navigation. ## Facts older examples get wrong - Next.js 16 enables the mode with top-level `cacheComponents: true`. It replaces `experimental.dynamicIO`, `experimental.useCache` and `experimental.ppr`; `cacheLife`/`cacheTag` import from `next/cache` without `unstable_`. In 16.4, set `partialPrefetching` explicitly: leaving it unset warns; recommended `create-next-app` defaults enable both flags. - With it enabled, segment exports `dynamic`, `revalidate`, `fetchCache` and `dynamicParams` are errors, and Edge runtime is unsupported. `revalidate = N` becomes `cacheLife` (nearest or custom profile) inside `use cache`; `force-dynamic` and `fetchCache` are unnecessary; fetch `next: { revalidate, tags }` becomes `cacheLife`/`cacheTag` in a `use cache` function; request data goes under Suspense; `dynamicParams = false` becomes `notFound()` for unknown params. - Existing `fetch` and `unstable_cache` caching still works as a separate layer, so do not mechanically rewrite every read. Its persistence across deployments and instances depends on retained/shared storage; self-hosted instances do not share it by default. `use cache` is in-memory by default; even a durable handler cannot reuse entries when the build/deployment ID changes. - `generateStaticParams` must return at least one param: `[]` fails the build, and removing the export renders the route on every request. - `revalidate: 0` or `expire` under 5 minutes makes a request-time hole instead of prerendered output; of the presets only `seconds` does. 16.3 adds `stale` thresholds (under 30 s: not prerendered; under 5 min: not in the App Shell). - Synchronous `new Date()`, `Math.random()` or `crypto.randomUUID()` during prerender is an error: capture it in `use cache`, or defer with `await io()` (`next/cache`, 16.3+) or `await connection()` (`next/server`) under Suspense. - `use cache: private` was experimental through 16.2. It runs at request time, stays out of the static shell and accepts no custom handler. - `export const instant` (16.3; `unstable_instant` in 16.2) opts a segment into instant-navigation validation. Since 16.3, every Page/Default segment is implicitly validated in `next dev` only (dev overlay) at the default level; build validation requires `experimental.instantInsights.validationLevel` set to `experimental-error` or `experimental-manual-error` (16.2 `unstable_instant` also validated at build). Test navigations with `@next/playwright` `instant()`. `instant = false` permits a blocking route; it does not clear synchronous-IO errors. For whole-app adoption or instant-navigation work, Next.js publishes workflow skills (`next-cache-components-adoption`, `next-cache-components-optimizer`) in `vercel/next.js/skills`; the bundled migration guide covers the same steps. ## Shape of one route `app/posts/page.tsx`, checked against next 16.3.8 with `cacheComponents: true` (`tsc --noEmit`, `next build`, the action under `next start`). `@/lib/*` is project code. ```tsx import { Suspense } from 'react' import { cookies } from 'next/headers' import { cacheLife, cacheTag, updateTag } from 'next/cache' import { insertPost, listPosts } from '@/lib/posts' // project code import { requireEditor } from '@/lib/auth' // throws unless the session may edit async function getPosts() { 'use cache' // shared entry: no cookies/headers/searchParams inside cacheLife('hours') cacheTag('posts') return listPosts() } async function Theme() { const theme = (await cookies()).get('theme')?.value // request-time, outside the cache return
Theme: {theme ?? 'system'}
} async function addPost(formData: FormData) { 'use server' await requireEditor() const title = String(formData.get('title') ?? '').trim() if (!title) return await insertPost(title) updateTag('posts') // Server Actions only; the next read waits for fresh data } export default async function Page() { const posts = await getPosts() // cached, so it can be part of the static shell return ( <>