cacheLife
Define named cache profiles that set stale, revalidate and expire in one call.
cacheLife?: { [profile: string]: { stale?: number; revalidate?: number; expire?: number } }Parameters
| Parameter | Type | Description |
|---|---|---|
$staleoptional | number= default profile (300) | Seconds the client can serve a cached value without checking the server. Optional; inherits the default profile's 5 minutes when omitted. |
$revalidateoptional | number= default profile (900) | Seconds after which the server refreshes the entry in the background; stale values may be served while it revalidates. Optional; inherits the default profile's 15 minutes when omitted. |
$expireoptional | number= default profile (never) | Maximum seconds a value may remain stale before the next request waits for fresh content. Optional; must be longer than revalidate. |
Errors
An expire shorter than revalidate is rejected, and the cacheLife() function throws if called at module scope or outside a cache directive scope.
Changelog
- PHP v16.0.0The cacheLife function became stable; the unstable_ prefix is no longer needed.
- PHP v16.0.0Requires cacheComponents, which replaced experimental.dynamicIO and experimental.useCache.
Each 'use cache' scope has three timings. stale is how long the client router reuses a value without asking the server. revalidate is how often the server refreshes it in the background. expire is the point at which a value is too old to serve and the next request waits. This config gives a set of those timings a name that cacheLife('name') can then use anywhere.
When to reach for it
Next.js already ships default, seconds, minutes, hours, days, weeks and max. Define your own when your content has a cadence those names do not describe — a blog profile, a pricing profile — so the intent is legible at every call site and one edit changes them all. You can also redefine a built-in, including default, which changes what an unannotated 'use cache' gets.
What trips people up
- Requires
cacheComponents: true. - Omitted properties inherit from
default, not from zero. - Short timings quietly change where content is served from:
revalidate: 0or anexpireunder 5 minutes drops the scope out of the prerender, and astaleunder 30 seconds does too, because a prefetch would expire before a click. - Redefining
hoursto mean something other than hours will mislead the next reader.
Example
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
cacheComponents: true,
cacheLife: {
blog: {
stale: 3600, // 1 hour
revalidate: 900, // 15 minutes
expire: 86400, // 1 day
},
},
}
export default nextConfigWhen to use it
- Give a content type its own cache policy — 'blog', 'pricing', 'inventory' — instead of repeating raw numbers.
- Change every editorial cache in the app by editing one profile.
- Redefine the default profile so unannotated 'use cache' scopes match your app's tolerance for stale data.
Frequently asked questions
What are the default cacheLife profiles in Next.js?
default (5 min stale, 15 min revalidate, never expires), seconds (30s/1s/1 min), minutes (5 min/1 min/1 hour), hours (5 min/1 hour/1 day), days (5 min/1 day/1 week), weeks (5 min/1 week/30 days) and max (5 min/30 days/1 year). Any of them can be redefined in next.config.
What is the difference between stale, revalidate and expire?
stale is how long the client router reuses a value without contacting the server, revalidate is how often the server refreshes it in the background while still serving the cached copy, and expire is the age at which a value can no longer be served so the next request waits for fresh content.
Why is my cached component not being prerendered?
Short timings exclude a scope from the prerender: a revalidate of 0 or an expire under 5 minutes makes it a request-time hole, and a stale under 30 seconds does the same because a prefetch would expire before the user could click. Of the presets only 'seconds' falls under these thresholds.
More examples
Using the profile at the call site
The name is resolved from next.config, and its type signature is generated during next dev, next build or next typegen — so editor autocomplete offers 'blog' alongside the presets and shows the timings you configured. cacheLife must be called inside the cached scope; at module scope it throws.
import { cacheLife } from 'next/cache'
export async function getCachedData() {
'use cache'
cacheLife('blog')
const res = await fetch('https://api.example.com/data')
return res.json()
}Redefining a built-in profile
Redefining default is supported and is the least surprising built-in to override, because the name implies no particular duration. It also changes the fallback for any scope that omits cacheLife entirely, so document it — otherwise a reader will assume the shipped 15-minute revalidate.
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
cacheComponents: true,
cacheLife: {
// Changes what every 'use cache' with no cacheLife() call gets
default: {
stale: 300, // 5 minutes
revalidate: 3600, // 1 hour
expire: 86400, // 1 day
},
},
}
export default nextConfigTwo profiles that read as intent
cacheLife('editorial') tells a reader which content policy applies without them decoding three numbers. When the editorial team asks for faster updates, one config edit moves every editorial scope at once.
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
cacheComponents: true,
cacheLife: {
editorial: {
stale: 600, // 10 minutes
revalidate: 3600, // 1 hour
expire: 86400, // 1 day
},
marketing: {
stale: 300, // 5 minutes
revalidate: 1800, // 30 minutes
expire: 43200, // 12 hours
},
},
}
export default nextConfig
Discussion