Caching in Next.js

Understand all four caching layers in Next.js — Request Memoisation, Data Cache, Full Route Cache, and Router Cache — and how they interact.

Last reviewed: January 2025

·Next.js 13Next.js 14Next.js 15
✅
Quick Understanding: Next.js has four caching layers. Most bugs come from not knowing which layer is serving stale data. The key insight: each layer can be independently controlled and invalidated. Master the four layers and caching bugs become obvious.

🔍 What Is It?

Next.js has four distinct caching mechanisms that work at different levels:

  1. Request Memoisation — deduplicates fetch calls within a single render pass. Scope: one request. Automatic.
  1. Data Cache — persists fetch results across requests and deployments. Scope: server-side, persistent. Controlled by cache and next.revalidate options.
  1. Full Route Cache — caches the rendered HTML and RSC payload for static routes. Scope: server-side, persistent. Invalidated by revalidation.
  1. Router Cache — client-side cache of RSC payloads for visited routes. Scope: browser session. Duration: 30s for dynamic, 5min for static (Next.js 14).

🎯 Why Do We Need It?

Each cache layer solves a specific performance problem:

  • →Request Memoisation — prevents duplicate DB queries when multiple components need the same data
  • →Data Cache — prevents hitting external APIs/DBs on every request
  • →Full Route Cache — serves pre-built HTML from CDN without server processing
  • →Router Cache — makes client-side navigation instant (no server round trip)

🏗️ What Happens in Real Projects?

Cache layers in a product page request:

  1. User navigates to /products/widget (client-side navigation)
  2. Router Cache checked — if visited recently, RSC payload returned instantly
  3. If not in Router Cache: server request made
  4. Full Route Cache checked — if static, pre-built HTML returned from CDN
  5. If dynamic: Server Component renders, calls fetchProduct(slug)
  6. Data Cache checked — if fetch was cached, return cached data
  7. If not cached: network request made
  8. Request Memoisation — if same fetch called twice in render, second call returns memoised result

⚙️ How It Works

Controlling each cache layer:

// Data Cache: controlled by fetch options
fetch(url, { cache: 'force-cache' })     // Cache indefinitely
fetch(url, { next: { revalidate: 60 } }) // Cache for 60s
fetch(url, { cache: 'no-store' })        // Never cache

// Full Route Cache: controlled by route segment config
export const dynamic = 'force-static';   // Always static
export const dynamic = 'force-dynamic';  // Always SSR
export const revalidate = 3600;          // ISR: 1 hour

// Router Cache: invalidated by
router.refresh(); // Invalidate current route
revalidatePath('/path'); // Server-side invalidation

💻 Example

Understanding and Controlling the Cache Layers — Practical examples of controlling each cache layer.

ts
// app/products/[slug]/page.tsx

// Option 1: Static with ISR (default for most product pages)
export const revalidate = 3600; // Revalidate every hour

// Option 2: Force dynamic (SSR) — for personalised content
export const dynamic = 'force-dynamic';

// Option 3: Force static — never dynamic, even if cookies() is called
export const dynamic = 'force-static';

// Option 4: Opt out of caching for dynamic params not in generateStaticParams
export const dynamicParams = true; // SSR unknown slugs (default)
export const dynamicParams = false; // 404 unknown slugs

🤔 Common Confusions

❓ Why is my data stale in development?

In development (next dev), the Data Cache and Full Route Cache are disabled. You always see fresh data. Caching only activates in production (next build + next start). Always test caching behaviour with a production build.

❓ I called revalidatePath but the page still shows stale data. Why?

revalidatePath invalidates the Data Cache and Full Route Cache (server-side). But the Router Cache (client-side) may still serve the old RSC payload. Call router.refresh() on the client after revalidation, or the Router Cache will expire on its own (30s for dynamic, 5min for static).

❓ Does the Router Cache persist across page refreshes?

No — the Router Cache is in-memory in the browser. A full page refresh (F5) clears it entirely. It only persists during a single browsing session without a hard refresh.

🚨 Common Production Issues

Stale data after mutations — the most common caching bug. After a write operation, invalidate both the server cache (revalidatePath/revalidateTag) and the client Router Cache (router.refresh()).

Data Cache not persisting on serverless — each serverless function invocation may have an empty Data Cache. Use a persistent external cache (Redis, Vercel KV) for cross-invocation caching.

Full Route Cache serving wrong user data — a dynamic route that reads cookies() should never be statically cached. Ensure dynamic routes are correctly identified as dynamic (check next build output).

✅ Best Practices

Understand which cache layer is responsible for each piece of data in your app

Test caching with next build + next start — development mode disables caching

After mutations: revalidatePath/revalidateTag (server) + router.refresh() (client)

Use the next build output to verify which routes are static vs dynamic

For multi-instance deployments, use an external cache store for the Data Cache

🏛️ Architect's Note

Senior Architect's Perspective

The four-layer cache is Next.js's most powerful feature and its most common source of bugs. The key is to make caching explicit and visible. Add comments to every fetch call explaining its cache strategy. Use the next build output as your caching audit — every route should be exactly the caching mode you intended.

💬 Frequently Asked Questions

How do I completely disable caching for debugging?

Set export const dynamic = "force-dynamic" on the page, and use cache: "no-store" on all fetch calls. In development, caching is already disabled. For production debugging, add a cache-busting query param or use the Vercel dashboard to purge specific paths.

What is the Router Cache duration in Next.js 14 vs 15?

Next.js 14: dynamic routes cached for 30s, static routes for 5 minutes. Next.js 15: the Router Cache opt-in behaviour changed — staleTimes can be configured in next.config.ts. Check the release notes when upgrading as this affects perceived data freshness after navigation.

Can I use Redis as the Data Cache store?

Yes — Next.js supports custom cache handlers via the cacheHandler option in next.config.ts. Libraries like @neshca/cache-handler provide Redis integration. This is essential for multi-instance deployments where each server needs to share the same cache.

🎯 Key Takeaways

1Four cache layers: Request Memoisation, Data Cache, Full Route Cache, Router Cache
2Each layer is independently controlled and invalidated
3Caching is disabled in development — always test with production builds
4After mutations: invalidate both server cache and client Router Cache
5The next build output shows which routes are static, dynamic, or ISR
6For multi-instance deployments, use Redis as a shared Data Cache store

🎯 Practice this topic in interviews

↗ Where this topic fits

Sources

Sources / References

Use these Next.js references to verify the concept in official docs and continue into the most relevant SplashRide pages.

Related SplashRide pages

SplashRide Next.js learning path

Browse the full Next.js topic collection and learning order on SplashRide.

Next.js Interview Prep

Practice Next.js interview questions and scenario-based answers.

Frontend Engineer Career Path

A beginner-friendly journey through web foundations, React, production frontend engineering, and interview preparation.

Full Stack Java Developer Career Path

A guided route through frontend foundations, Java backend engineering, delivery workflows, and interview readiness.

Incremental Static Regeneration (ISR)

Master ISR — the Next.js feature that gives you static performance with dynamic freshness. Learn time-based and on-demand revalidation for real production systems.

Revalidation

Master Next.js revalidation — time-based, on-demand, and tag-based strategies for keeping cached content fresh without sacrificing performance.