The Four Caching Layers in Next.js
Next.js layers four distinct caching mechanisms that each solve a different problem: Request Memoization dedupes identical fetch calls within a single render pass on the server; the Data Cache persists fetch results across requests and deployments on the server; the Full Route Cache stores the rendered HTML and RSC payload for statically rendered routes at build time; and the Router Cache stores route segment payloads in the browser's memory for instant back/forward navigation. Understanding which layer is responsible for stale data is the single most common source of confusion when debugging a Next.js app that 'won't update.'
Cricket analogy: Like a cricket broadcast having separate systems for the live scorer's notepad, the official archive scorecard, the stadium's printed program, and a fan's own memory of who's batting — each serving a different purpose.
When debugging 'stale data' in Next.js, always ask which of the four layers is holding the old value: was the fetch memoized within one render, cached in the Data Cache, baked into the Full Route Cache, or just sitting in a visitor's client-side Router Cache?
The Data Cache: Persisting fetch Results
The Data Cache stores the results of fetch requests on the server so that, by default, a fetch call is cached indefinitely across requests and even across deployments unless you specify otherwise; you opt out per-call with { cache: 'no-store' } or set a time-based expiry with { next: { revalidate: 3600 } }. Tagging a fetch with { next: { tags: ['products'] } } lets you later invalidate exactly that cached entry on demand with revalidateTag('products') without waiting for its time-based expiry.
Cricket analogy: Like a stadium's ground staff keeping yesterday's pitch report on file indefinitely unless someone specifically orders a fresh pitch inspection before the next match.
// Tagged, time-based Data Cache entry
async function getProducts() {
const res = await fetch('https://api.store.com/products', {
next: { revalidate: 3600, tags: ['products'] },
});
return res.json();
}
// Opting a specific fetch out of the Data Cache entirely
async function getLiveInventory(sku: string) {
const res = await fetch(`https://api.store.com/inventory/${sku}`, {
cache: 'no-store',
});
return res.json();
}Full Route Cache and Static Rendering
Routes that don't rely on dynamic APIs like cookies() or searchParams are statically rendered at build time and their HTML plus RSC payload are stored in the Full Route Cache, meaning every visitor to /blog/hello-world gets served the exact same pre-rendered response until it's revalidated or the app is redeployed. Setting export const dynamic = 'force-dynamic' on a page, or using a dynamic function anywhere in its render tree, opts that specific route out of the Full Route Cache and forces server rendering on every request instead.
Cricket analogy: Like a printed match program distributed to the whole stadium ahead of time, identical for every seat, versus a personalized replay clip generated specifically for one fan's phone.
Using a dynamic function such as cookies(), headers(), or searchParams anywhere in a page's component tree — even deep inside a nested Server Component — opts the entire route out of the Full Route Cache, not just that one component.
The Router Cache: Client-Side Navigation
The Router Cache lives entirely in the browser's memory and stores the RSC payload for route segments you've already visited, so clicking back to a previously visited page renders instantly from that in-memory cache instead of re-fetching from the server; static segments are cached by default for a long period while dynamic segments have a much shorter cache window. Because this cache lives client-side, a server-side revalidateTag() call won't automatically clear a stale entry sitting in a user's Router Cache until they trigger a hard navigation or the cache's own timer expires.
Cricket analogy: Like a fan who already watched an over live keeping that memory fresh in their head, so replaying it in their mind is instant, unlike waiting for the broadcaster to re-air it.
- Request Memoization dedupes identical fetch calls within a single server render pass.
- The Data Cache persists fetch results on the server across requests and deployments, by default indefinitely.
- Tagging a fetch with next.tags lets you invalidate exactly that entry later with revalidateTag.
- The Full Route Cache stores a statically rendered route's HTML and RSC payload at build time.
- Any dynamic function used anywhere in a route's component tree opts the whole route out of the Full Route Cache.
- The Router Cache lives in the browser and makes back/forward navigation between visited routes instant.
- A server-side revalidateTag call does not automatically clear a stale Router Cache entry already in a user's browser.
Practice what you learned
1. Which caching layer dedupes identical fetch calls made within a single server render pass?
2. By default, how long does the Data Cache persist a fetch result?
3. What opts an entire route out of the Full Route Cache?
4. Where does the Router Cache live?
5. Why might a user still see stale data after a server-side revalidateTag call?
Was this page helpful?
You May Also Like
Revalidation Strategies
Understand time-based revalidation (ISR) and on-demand revalidation with revalidatePath and revalidateTag, and how to wire them into Server Actions and Route Handlers.
Fetching Data in Server Components
Learn how to fetch data directly inside React Server Components using async/await, and how Next.js handles deduplication, parallelization, and streaming for those requests.
Route Handlers (API Routes)
Learn how to build server-side API endpoints in the Next.js App Router using route.ts files, the Web Request/Response APIs, dynamic segments, and their caching behavior.
Server Actions Explained
Learn how Server Actions let you write server-side mutations that are callable directly from forms and Client Components, with progressive enhancement, pending states, and security considerations.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentSpring Boot Study Notes
Java · 30 topics
Web DevelopmentFlask Study Notes
Python · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics