Home/Learn/Next.js/The Caching Layers

The Caching Layers

Advanced
Data & Caching

Four caches sit between a request and your data, each with its own lifetime and its own way of being wrong. Naming them is what makes a staleness bug diagnosable.

Overview

Next.js caches aggressively, which is why it is fast and why "my data is stale" is the most common complaint about it. The confusion comes from there being four separate caches rather than one, operating at different layers and cleared by different things. Once you can name which layer is serving a value, the fix is obvious. This is also the area Next 15 changed most: the defaults went from cached-by-default to uncached-by-default, so advice written for 14 actively misleads on 15.

The Four

Where each lives, what it stores and how long.

Memoization, data, full route, router
// 1. REQUEST MEMOIZATION — React, per render pass, server
//    Dedupes identical fetch/cache() calls in one render.
//    Lifetime: one render. Not configurable, and never a bug.

// 2. DATA CACHE — Next, persistent, server, across requests AND deploys
//    Stores the RESULT of a fetch.
//    Next 14: cached by default.  Next 15: NOT cached by default.
fetch(url, { cache: 'force-cache' })          // opt in (15)
fetch(url, { cache: 'no-store' })             // opt out (14)
fetch(url, { next: { revalidate: 60 } })      // time-based
fetch(url, { next: { tags: ['problems'] } })  // taggable

// 3. FULL ROUTE CACHE — Next, build/server, the rendered HTML and RSC
//    payload of a STATIC route. Cleared by a deploy or revalidation.

// 4. ROUTER CACHE — the browser, in memory, per session
//    Rendered segments from routes you visited.
//    Next 14: 30s dynamic / 5min static.  Next 15: 0 / 5min.

// Reading order on a request:
//   router cache -> full route cache -> data cache -> your source

// Diagnostic that saves an hour: does a HARD REFRESH show fresh data?
//   yes -> the router cache (client)
//   no  -> the data cache or the full route cache (server)

What Makes a Route Uncacheable

Dynamic APIs opt a route out, sometimes by accident.

One cookies() call can turn a whole subtree dynamic
// Touching any of these makes the whole route dynamic:
cookies()          headers()          draftMode()
searchParams       (as a page prop)
fetch(..., { cache: 'no-store' })
export const dynamic = 'force-dynamic'

// So one call to cookies() in a shared layout makes EVERY page under
// it dynamic — a common and invisible performance regression. Check
// the build output: it labels each route.
//   ○  (Static)   prerendered
//   ●  (SSG)      prerendered with generateStaticParams
//   ƒ  (Dynamic)  server-rendered on demand

// If a page went from ○ to ƒ and you did not intend it, something in
// its tree started reading a dynamic API.

// Contain it rather than accepting it: move the cookie read into a
// small component with its own Suspense boundary, and the rest of the
// route stays static. In Next 15 with PPR this is the standard shape.

// unstable_noStore() (14) / connection() (15) force dynamic for a
// specific subtree without a route-level flag.

Version Differences That Bite

Next 14 and 15 disagree on the defaults, and most blog posts predate the change.

Be explicit; the defaults flipped in 15
// Next 14 (this codebase, at 14.2.x)
fetch(url)                       // CACHED indefinitely by default
// -> the classic "why is my API data frozen" report
fetch(url, { cache: 'no-store' })          // the fix

// Next 15
fetch(url)                       // NOT cached by default
fetch(url, { cache: 'force-cache' })       // opt in
// Route handlers (GET) also became uncached by default.

// Because of this, always be explicit rather than relying on the
// default. It documents intent and it survives an upgrade:
fetch(url, { cache: 'no-store' })                 // live data
fetch(url, { next: { revalidate: 3600 } })        // hourly
fetch(url, { next: { tags: ['problems'] } })      // on-demand

// And check the version before trusting any advice you read:
//   package.json -> "next": "14.2.3"

// The dev server does not behave like production here. Verify with
// npm run build && npm start before concluding anything about caching.

Key Points to Remember

  • 1Four caches: request memoization, the data cache, the full route cache and the client router cache
  • 2A hard refresh distinguishes them — fresh after a hard refresh means the client router cache is at fault
  • 3Reading cookies, headers or searchParams anywhere in a tree makes the whole route dynamic
  • 4The build output labels each route static or dynamic, which is how you catch an accidental regression
  • 5Next 14 caches fetch by default and Next 15 does not — always set cache or revalidate explicitly

Interview Questions

Sign in to ask Aria
1

Name the caching layers in the App Router and what each stores.

Hard
2

How do you tell whether stale data is coming from the server or the client?

Hard
3

Why might adding cookies() to a layout slow down every page beneath it?

Hard

Ask Aria about The Caching Layers

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…