---
name: nextjs-16-cache-components
description: "Next.js 16 Cache Components — 'use cache', PPR layouts, cacheLife/cacheTag, Turbopack, legacy cache migration. Production Next.js 16 caching: 'use cache' boundaries on components, functions, and route segments, with cacheLife profiles and cacheTag for revalidateTag invalidation. Designs PPR-friendly layouts that split static shells from dynamic holes via Suspense, migrates off legacy unstable_cache/fetch-cache and route-segment config, handles the cacheComponents flag and Turbopack-default builds, and updates middleware to proxy.ts."
license: MIT
---

# Next.js 16 Cache Components

A focused skill for shipping the new caching model in Next.js 16. It helps you adopt the `'use cache'` directive, wire up `cacheLife` and `cacheTag`, design Partial Prerendering (PPR) layouts that separate static shells from dynamic holes, and migrate code off the older `unstable_cache` / fetch-based `revalidate` patterns. Built for teams running the App Router on Turbopack who want explicit, predictable caching instead of implicit fetch memoization.

## When to use it

- You're upgrading an App Router app to Next.js 16 and the implicit fetch cache no longer behaves as before.
- You want a page to render an instant static shell while streaming personalized or slow data underneath.
- You need tag-based invalidation so a single mutation (`revalidateTag('product-42')`) clears exactly the right cached output.
- You're replacing scattered `unstable_cache(...)` calls and `export const revalidate` exports with `'use cache'` boundaries.

## How it works

The core primitive is the `'use cache'` directive, placed at the top of a file, an async function, or a component to mark its output as cacheable. Inside a cached scope you call:

- `cacheLife('hours')` (or `minutes`, `days`, or a custom profile defined in `next.config.ts`) to set stale/revalidate/expire windows.
- `cacheTag('user-' + id)` to attach tags, then invalidate with `revalidateTag(...)` from a Server Action or route handler.

For PPR, the skill structures layouts so static content prerenders at build time while dynamic segments wrapped in `<Suspense>` become runtime "holes." It also audits for accidental dynamic APIs (`cookies()`, `headers()`, `searchParams`) leaking into cached scopes, which forces a whole boundary dynamic.

## Usage

```ts
// app/products/[id]/page.tsx
import { cacheLife, cacheTag } from 'next/cache'

async function getProduct(id: string) {
  'use cache'
  cacheLife('hours')
  cacheTag(`product-${id}`)
  return db.product.find(id)
}

export default async function Page({ params }) {
  const { id } = await params
  const product = await getProduct(id)
  return <ProductView product={product} />
}
```

```ts
// invalidate after a mutation
'use server'
import { revalidateTag } from 'next/cache'
export async function updatePrice(id: string) {
  await db.product.update(id, ...)
  revalidateTag(`product-${id}`)
}
```

## Tips & gotchas

- `'use cache'` requires `cacheComponents: true` in `next.config.ts`; arguments to a cached function must be serializable since they become part of the cache key.
- Reading `cookies()` or `headers()` inside a cached boundary is an error — pass the needed value in as an argument instead.
- `cacheTag` only works inside a `'use cache'` scope; calling it elsewhere throws.
- Closure variables captured by a cached function also join the key, so avoid pulling in large or non-deterministic objects.

## FAQ

**Do I still need `unstable_cache`?** No. `'use cache'` replaces it and supports tagging and lifetimes natively; the skill rewrites existing calls.

**Is fetch still auto-cached?** Not by default in this model — wrap data access in `'use cache'` to opt in explicitly.

**Does this require Turbopack?** Cache Components target the Turbopack-based Next.js 16 build, which the skill assumes.
