cacheLife

Define named cache profiles that set stale, revalidate and expire in one call.

Signature
cacheLife?: { [profile: string]: { stale?: number; revalidate?: number; expire?: number } }

Parameters

ParameterTypeDescription
$staleoptionalnumber= 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.
$revalidateoptionalnumber= 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.
$expireoptionalnumber= 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: 0 or an expire under 5 minutes drops the scope out of the prerender, and a stale under 30 seconds does too, because a prefetch would expire before a click.
  • Redefining hours to mean something other than hours will mislead the next reader.

Example

Example · typescript
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 nextConfig

When 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.

Example · typescript
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.

Example · typescript
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 nextConfig

Two 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.

Example · typescript
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

  • Be the first to comment on this lesson.