Skip to content

The Four Caching Layers

Next.js caching is four separate layers — Request Memoization, the Data Cache, the Full Route Cache, and the Router Cache — and since v15 the Data Cache is opt-in, because fetch is no longer cached by default.

Each layer caches a different thing, lives in a different place, and is invalidated in a different way. Keeping them separate in your head is the whole skill.

LayerWhat it cachesWhere it livesLifetimeHow it is invalidated
Request MemoizationReturn value of identical fetch / cache() calls in one renderServer, per request (React)A single request / render passEnds automatically when the render finishes
Data CacheResults of data fetches, reused across requests and deploysServer (persistent)Until revalidatedrevalidateTag, revalidatePath, or time-based revalidate
Full Route CacheRendered RSC payload plus HTML of a statically-rendered routeServer (persistent, build or runtime)Until a deploy or the underlying data revalidatesRevalidating the underlying data, or a redeploy
Router CacheRSC payloads of visited and prefetched routesClient (browser memory)The session; staleTimes controls freshnessNavigation, router.refresh(), or a Server Action revalidation

Server-side: memoization, data, and route caches

Section titled “Server-side: memoization, data, and route caches”

Three of the four layers live on the server, and they stack. Request Memoization dedupes identical calls inside one render. The Data Cache persists fetch results across requests. The Full Route Cache stores the fully-rendered output of a static route so a request can skip rendering entirely.

The catch beginners miss: since Next.js 15, a plain fetch writes nothing to the Data Cache. You opt in per call.

// Not cached — hits the origin on every request (the Next.js 15+ default)
await fetch('https://api.example.com/prices')
// Opt in to the Data Cache indefinitely
await fetch('https://api.example.com/config', { cache: 'force-cache' })
// Opt in with a 60-second lifetime
await fetch('https://api.example.com/feed', { next: { revalidate: 60 } })
// Opt in and tag for on-demand invalidation
await fetch('https://api.example.com/cart', { next: { tags: ['cart'] } })

The fourth layer lives in the browser. When you navigate or hover a prefetched <Link>, Next.js keeps the route’s RSC payload in memory so back and forward are instant. Since v15 the default staleTimes.dynamic is 0, so dynamic pages are not held client-side unless you raise it.

// next.config.ts — opt into holding dynamic routes for 30s in the Router Cache
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
staleTimes: { dynamic: 30 },
},
}
export default nextConfig

Why “fetch is uncached by default” matters

Section titled “Why “fetch is uncached by default” matters”

In Next.js 14, fetch defaulted to force-cache, so tutorials taught “Next.js caches everything automatically.” That is wrong for v15 and v16. Today caching is layered and opt-in: an uncached fetch makes the route dynamic and the Data Cache stays empty until you ask for it. GET Route Handlers changed the same way — they are dynamic unless you add export const dynamic = 'force-static'.

flowchart TD
  A["Browser navigates"] --> B["Router Cache (client)"]
  B -->|miss| C["Full Route Cache (server)"]
  C -->|miss or dynamic| D["Render Server Components"]
  D --> E["Request Memoization (per render)"]
  E --> F["Data Cache (server)"]
  F -->|miss| G["Origin: API or database"]
How a request falls through the four caches
Since which version is fetch uncached by default in Next.js?
Which of the four layers lives in the browser?
Which layer dedupes identical fetch calls within a single render?
What does revalidateTag invalidate?