Introduction
Next.js App Router features a robust caching architecture designed to optimize loading speeds and reduce backend API overhead.
However, because Next.js integrates multiple caching layers, developers often run into debugging issues. Common issues include pages displaying stale data or APIs firing too frequently.
This article clears up the confusion by explaining the four caching mechanisms in Next.js, along with techniques to configure, refresh, and invalidate them.
1. The Four Caching Layers of Next.js App Router
The Next.js App Router uses four separate caching layers:
[Client Side]
1. Router Cache (Temporary in-browser navigation cache)
│
[Server Side]
2. Request Memoization (React component render-cycle memory)
│
3. Data Cache (Persistent API request data across users)
│
4. Full Route Cache (Static HTML and RSC payloads at build-time)
Here is a summary of the characteristics of each layer:
| Cache Layer | Location | Cached Content | Lifespan | Invalidation / Revalidation |
|---|---|---|---|---|
| 1. Router Cache | Client | RSC payload | Session-based (minutes) | router.refresh(), Server Actions |
| 2. Request Memoization | Server | fetch responses | Single render cycle | Automatically cleared per request |
| 3. Data Cache | Server | API response data | Persistent | revalidatePath, revalidateTag |
| 4. Full Route Cache | Server | HTML & RSC payloads | Persistent | Rebuilds, tied to Data Cache |
2. How Each Cache Layer Works
① Request Memoization
If the same URL and configuration are fetched multiple times within the same React component tree during a single render request, only the first request is executed. The remaining calls read from the memoization cache.
// Component A
async function UserProfile() {
const res = await fetch('https://api.example.com/user'); // Fires API call
return <div>...</div>;
}
// Component B (rendered during the same cycle)
async function UserSettings() {
const res = await fetch('https://api.example.com/user'); // Reads from memoization cache
return <div>...</div>;
}
- The Benefit: Eliminates the need to pass data down via props. Components can fetch their own data requirements independently.
② Data Cache
The Data Cache persists API response data across incoming requests and multiple users.
By default, standard fetch requests in Next.js 14 are cached here (note: Next.js 15 defaults to uncached fetch requests, though caching can be enabled explicitly).
- Revalidation:
You can set a time-based TTL or trigger on-demand revalidation:
// Revalidate cache after one hour (3600 seconds) fetch('https://api.example.com/products', { next: { revalidate: 3600 } });
③ Full Route Cache
At build time, Next.js compiles page structures, rendering Static HTML and RSC payloads. This corresponds to Static Site Generation (SSG).
- Dynamic Interruptions:
If Next.js detects dynamic functions (such as
cookies(),headers(), or reading dynamicsearchParams), it bypasses the Full Route Cache. The page is then rendered dynamically on every request.
④ Router Cache
A client-side browser cache. When users navigate between routes, Next.js caches pre-fetched and visited pages in memory. This enables instant back-and-forth navigation.
- Invalidation:
Triggering
revalidatePathinside a Server Action automatically invalidates the Router Cache, ensuring the user gets the updated layout on their next navigation.
3. Practical Cache Configuration Recipes
Scenario A: Real-Time Data (Bypassing Server Cache)
For pages displaying real-time updates (like financial charts or live status boards), disable caching on fetch requests:
const res = await fetch('https://api.example.com/live-ticks', {
cache: 'no-store'
});
Alternatively, force the entire route to remain dynamic:
export const dynamic = 'force-dynamic';
Scenario B: On-Demand Revalidation (revalidateTag)
Useful for updating cached content immediately when a database record changes (such as publishing a blog post).
// Append a tag to the fetch cache
const res = await fetch('https://api.example.com/posts', {
next: { tags: ['blog-posts'] }
});
// Trigger cache invalidation in a Server Action
import { revalidateTag } from 'next/cache';
async function updatePost() {
'use server';
await saveToDatabase();
// Clear all caches tagged with 'blog-posts'
revalidateTag('blog-posts');
}
Conclusion
Understanding Next.js caching relies on distinguishing the lifespan of Request Memoization (one render request) from the Data Cache (persistent across requests).
To verify your caching setup, run next build before deploying to inspect which routes are compiled as static (○) and which are dynamic (λ).
