Lesson 11 of 81
A blog doesn't need to be rebuilt every time a post changes, and it doesn't need to be dynamically rendered on every request either — ISR sits between those two extremes, serving stale content instantly while a fresh copy regenerates in the background.
11 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.
Question 1 of 3
A page has `export const revalidate = 3600`. A request arrives 5 minutes after that window expired. What does that visitor see?
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
Incremental Static Regeneration (ISR) lets a statically rendered page update itself after deployment, without a full rebuild. The classic, route-segment-level way to enable it is a single export:
export const revalidate = 3600; // seconds
This works whether or not Cache Components (cacheComponents: true) is enabled — it's the older, simpler caching model, and it's still fully present and supported in Next.js 16 for routes that aren't using "use cache".
The behavior is stale-while-revalidate: the first request after the revalidate window passes still gets the existing cached page immediately — the visitor never waits — while Next.js regenerates a fresh version in the background. Once that regeneration finishes, the new version replaces the cached one and every subsequent visitor gets the updated content. No visitor ever blocks on a rebuild; they either get the current cache or the previous one, never a loading spinner for the regeneration itself.
// app/blog/page.tsx
interface Post {
id: string;
title: string;
}
export const revalidate = 3600; // invalidate at most once per hour
export default async function BlogPage() {
const data = await fetch("https://api.example.com/blog");
const posts: Post[] = await data.json();
return (
<main>
<h1>Blog Posts</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</main>
);
}
For the first hour after this page is built or last regenerated, every request gets the same cached HTML, instantly, straight from cache. The first request after that hour passes still gets that same (now stale) cached HTML immediately — but it also triggers a background regeneration. Once that finishes, the cache is replaced, and the next visitor after that gets the fresh version. If an error happens during that background regeneration, Next.js keeps serving the last successfully generated version and just retries on the next request, rather than showing visitors a broken page.
revalidate must be a statically analyzable literal. export const revalidate = 600 is valid; export const revalidate = 60 * 10 is not — Next.js needs to read the value without executing arbitrary code.revalidate has no visible effect until a real production build (next build && next start), which is exactly why testing ISR behavior in dev mode is misleading.fetch() calls in the same route have different revalidate values, the lowest one wins for the whole route — a single fast-changing fetch forces more frequent regeneration for everything else on that page too.revalidateTag/revalidatePath) and time-based revalidate aren't mutually exclusive. A page can have a one-hour time-based fallback and also get invalidated immediately after a specific mutation — whichever happens first wins."Why didn't the page update immediately after I changed revalidate and made a request?" tests whether a candidate actually understands stale-while-revalidate semantics — the correct answer is that the very next request after the window closes still gets stale content and merely triggers the regeneration, it doesn't wait for it.