Under The Hood
frontend nextjs Next.js 14+/15 App Router

Next.js App Router: the caching layers

Last updated
Prerequisites:
Next.js App Router basics
Familiarity with fetch and cache-control semantics
  • nextjs
  • caching
  • app-router
  • cache
  • revalidation
  • ssg

Read at your depth

The practical view

The App Router has several caches with distinct lifetimes: the full route cache (prerendered HTML stored on the server/CDN, revalidated by revalidatePath/time-based revalidate); the data cache (persisted fetch() results keyed by URL + options, controlled by next: { revalidate } or fetch's cache); the router cache (client-side cache of visited segments and prefetched payloads, invalidated on navigation to stale-while-revalidate pages); and React's request memoization (dedupes identical fetches within one render pass, always fresh within the request). The practical rule: add noStore()/no-store to anything user-specific, use revalidateTag('x') after mutations, and remember the router cache is per-session on the client.

The same idea in other frameworks

react equivalent
// React 19: <Cache> component, cache() for memoization;
// framework (Next) owns fetch caching and hydration

Legacy vs modern

Stale user data vs correctly revalidated data

A default fetch caches aggressively and can leak cross-user data; opting the route out and tagging mutations keeps data both fast and correct.

before → after
Uncontrolled default caching
const res = await fetch('https://api.example.com/me');
const me = await res.json();
Explicit cache strategy
const res = await fetch('https://api.example.com/me', { next: { revalidate: 60 } });

Interview gotchas

Context

This is the canonical App Router correctness question — the interviewer wants the caching layers AND the authorization pitfall in one answer.

The mechanical answer

A page with no dynamic signals (no cookies/headers reads, no dynamic config) can be prerendered and its output stored in the full route cache, which may be served to any user. If the data was fetched with the default cache policy, the data cache can also persist a response keyed only by URL+options — not by the auth token — so user B can receive user A's cached payload. The fix is to make the page dynamic (read cookies()/headers(), or set dynamic = 'force-dynamic', or noStore()) and to include the auth token in the fetch key (headers make the cache key distinct) or bypass caching entirely for personal data. Cache tags then scope revalidation to that user's entries.

Trap

The trap answer is 'just add no-store to every fetch'. That sacrifices the pipeline's value and does not fix the full route cache (the HTML can still be cached and served to others). The sharp answer names both layers: per-user pages need dynamic rendering AND per-user cache keys or explicit no-store. Candidates who mention that cache tags + dynamic signals work together ('personalized data is dynamic, public data is cached and tag-revalidated') demonstrate production-grade understanding.

Under The Hood — a multi-depth technical interview hub.

Third-party licenses Press ⌘ K to search.