Lesson 41 of 81
'The page is stale' can mean three completely different things depending on which of Next.js's caches is actually holding onto old data. Knowing which one you're looking at is the difference between a five-second fix and an hour of guessing.
13 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
Earlier lessons covered fetch() caching, "use cache", ISR, and on-demand revalidation individually. This lesson is the map connecting them: Next.js (without cacheComponents enabled) actually has up to three separate caching layers, each solving a different problem, each cleared a different way, and a bug report like "this isn't updating" can be sitting in any one of them.
fetch() results (or unstable_cache() calls), keyed by URL and options. This is what cache: 'force-cache' and next: { revalidate, tags } control. It survives across requests and across users — one fetch result can serve every visitor.export const revalidate = N and ISR operate on. A route can be statically rendered even when it makes cached fetch() calls — the Full Route Cache is caching the result of rendering, one layer above the Data Cache it was built from.<Link>. This is why navigating back to a page you were just on can feel instant with no server round trip at all — and why a server-side revalidation doesn't always show up immediately without a client-side signal to also clear this cache.// app/products/[id]/page.tsx
export const revalidate = 3600; // Full Route Cache: refresh at most hourly
async function getProduct(id: string) {
// Data Cache: force-cache + a tag, independent lifetime from the route's
const res = await fetch(`https://api.example.com/products/${id}`, {
cache: "force-cache",
next: { tags: [`product-${id}`] },
});
return res.json();
}
export default async function ProductPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
const product = await getProduct(id);
return <ProductView product={product} />;
}
An admin updates this product's price in the CMS. Three different fixes apply to three different symptoms:
revalidateTag(\product-$`, 'max')` from the mutation so the next fetch gets fresh data.revalidatePath('/products/[id]', 'page') clears the cached render itself.revalidatePath/revalidateTag run inside a Server Action triggered from that same page, but a webhook-driven update from an external system has no such automatic client-side signal to send.revalidateTag when the actual problem is a stale Router Cache entry in one specific browser tab wastes time diagnosing the wrong layer.cookies(), headers(), uncached searchParams usage) opts the whole route out of the Full Route Cache, falling back to dynamic rendering on every request — at which point only the Data Cache layer is still relevant for that route.cacheComponents/"use cache". A codebase can be entirely on the classic model (this lesson), entirely on Cache Components, or migrating between the two; "which caching model is this route even using" is the first question, before picking which cache to blame."A user says a page shows stale data after an admin update, and you've already confirmed the database has the new value — where do you start looking?" tests whether a candidate has an actual mental model of Next.js's layered caching, or just knows individual API names without understanding which layer each one clears — the difference between naming revalidateTag and knowing why it might not be the right tool for this particular symptom.