Next.js has multiple caching layers — Request Memoization deduplicates identical fetches in a single render, the Data Cache persists fetch results across requests, the Full Route Cache stores rendered HTML/RSC payload at build time, and revalidation (time-based or on-demand) controls when cached data refreshes.
Request Memoization (per-render), Data Cache (persistent fetch), Full Route Cache (pre-built HTML), Router Cache (client navigation) — each serves a different purpose.
fetch options control caching: cache: 'no-store' skips cache, next.revalidate sets time interval, next.tags enables on-demand invalidation.
Time-based (ISR): stale-while-revalidate after an interval. On-demand: revalidatePath() and revalidateTag() for immediate invalidation when data changes.
Routes are static (cached at build) by default. Using cookies(), headers(), searchParams, or no-store makes routes dynamic (rendered per request).
Caching is one of the most complex and important aspects of Next.js. The framework has multiple caching layers that work together to optimize performance, and understanding each layer is essential for controlling data freshness.
React automatically deduplicates identical fetch() calls during a single server render. If multiple Server Components call fetch('https://api.com/user/1') with the same URL and options, only one network request is made — the result is shared across all components in that render pass.
This means you can call the same data-fetching function in multiple components without worrying about redundant requests. Memoization lasts only for one render — it doesn't persist across requests.
The Data Cache persists fetch results across requests and deployments on the server. When a Server Component calls fetch(), the response is stored in the Data Cache. Subsequent requests for the same URL return the cached result without hitting the external API.
Control the Data Cache per fetch:
fetch(url, { cache: 'force-cache' }) — cache indefinitely (default for static rendering)fetch(url, { cache: 'no-store' }) — skip cache, always fetch freshfetch(url, { next: { revalidate: 3600 } }) — cache for 1 hour, then revalidatefetch(url, { next: { tags: ['users'] } }) — tag for on-demand revalidationAt build time, Next.js renders static routes and caches the HTML and React Server Component Payload. When a user requests a static page, they get the pre-built cached version — no server rendering needed. This is the fastest possible response.
Dynamic routes (those using cookies(), headers(), searchParams, or cache: 'no-store') skip the Full Route Cache and render on every request.
The browser caches visited route segments in memory during the session. When navigating between pages, previously visited routes load instantly from the client-side cache. Prefetched routes (links in the viewport) are also stored in the Router Cache.
The Router Cache duration varies: dynamic pages are cached for 30 seconds, static pages for 5 minutes. router.refresh() invalidates the Router Cache for the current route.
ISR (Incremental Static Regeneration) revalidates static pages after a time interval:
// Page-level revalidation
export const revalidate = 3600; // revalidate every hour
// Per-fetch revalidation
fetch(url, { next: { revalidate: 3600 } });After the revalidation period, the next request triggers a background regeneration. The stale page is served immediately while the new version is built — stale-while-revalidate pattern.
For immediate cache invalidation (e.g., after a CMS update), use revalidatePath() or revalidateTag():
import { revalidatePath, revalidateTag } from 'next/cache';
// In a Server Action or Route Handler
revalidatePath('/blog'); // Invalidate a specific path
revalidateTag('posts'); // Invalidate all fetches tagged 'posts'This is more precise than time-based revalidation — you invalidate exactly when data changes.
fetch() in Server Components is cached by default — stale data in developmentcache: 'no-store' on any fetch makes the entire route dynamicNext.js has four caching layers: Request Memoization (per-render dedup), Data Cache (persistent fetch results), Full Route Cache (pre-built HTML), and Router Cache (client-side navigation). Revalidation can be time-based (ISR) or on-demand (revalidatePath/revalidateTag). cache: 'no-store' opts out of caching; next.revalidate sets a time interval; next.tags enables targeted invalidation.
Fun Fact
Next.js caching has been the most controversial feature in the framework's history. The aggressive default caching in Next.js 14 (where fetch was cached by default) caused so much developer confusion that Next.js 15 reversed the default — fetch is now uncached by default. The caching documentation page is the most-visited page in the Next.js docs.