Explicit caching is how Next.js 16 apps get static speed for data that changes rarely, without stale-data surprises.
The `'use cache'` directive caches the return value of an async function, component or whole page. Put it at the top of the function body (or the top of a file to cache all its exports).
- Data level:
async function getProducts() { 'use cache'; ... }shares the result wherever it's used. - UI level: a cached Server Component or page caches its rendered output.
The cache key is built automatically from the function's identity, its arguments and any values it captures from outer scopes, so getProduct('a') and getProduct('b') are separate entries. Arguments and return values must be serialisable. You can't call cookies() or headers() inside a cached scope; read them outside and pass the values in as arguments.
`cacheLife(profile)` sets the lifetime. Built-in profiles are seconds, minutes, hours, days, weeks and max (or a custom { stale, revalidate, expire } object). Without it the default profile applies (stale 5 min, revalidate 15 min). Very short lifetimes are treated as request-time holes rather than baked into the static shell.
`cacheTag('products')` labels entries so you can invalidate them on demand.
Variants: 'use cache: remote' stores entries in a shared, durable cache handler (the default store is in-memory per instance), and 'use cache: private' caches per user data that reads cookies, only in the browser.
Example
// lib/catalog.ts
import { cacheLife, cacheTag } from 'next/cache'
import { db } from '@/lib/db'
export async function getProducts(category: string) {
'use cache'
cacheLife('hours') // fresh for an hour, then revalidated in background
cacheTag('products', `category:${category}`)
return db.product.findMany({ where: { category } }) // key includes `category`
}
// app/shop/[category]/page.tsx
import { getProducts } from '@/lib/catalog'
async function ProductGrid({ category }: { category: string }) {
'use cache' // cache the rendered UI too
cacheLife('hours')
const products = await getProducts(category)
return <ul>{products.map((p) => <li key={p.id}>{p.name}</li>)}</ul>
}
export default async function Shop({ params }: PageProps<'/shop/[category]'>) {
const { category } = await params
return <ProductGrid category={category} />
}Remember
'use cache'caches a function, component or page; arguments and captured values form the keycacheLifesets stale/revalidate/expire;cacheTaglabels entries for invalidation- Read cookies/headers outside and pass values in;
use cache: remote/privateare variants
Watch out for
- Calling
cookies()inside a'use cache'function; runtime APIs aren't allowed in cached scopes.