Incremental Static Regeneration (ISR) in Next.js
SSG gives you speed but content goes stale until the next full rebuild. SSR gives you freshness but costs real server computation on every request. Incremental Static Regeneration exists precisely to give you both — the speed and low cost of serving static files, combined with content that automatically refreshes without requiring a full manual rebuild and redeploy of your entire site.
ISR works by letting you attach a revalidation policy to a statically generated page: either a time interval ('regenerate this page in the background at most once every N seconds') or an on-demand trigger ('regenerate this specific page right now, because I know its data just changed'). This lesson covers both approaches in depth, since together they solve the freshness problem that plain SSG can't address on its own.
Learning Objectives
- Explain how Incremental Static Regeneration blends SSG and SSR.
- Use the revalidate option to set a time-based regeneration interval.
- Understand what 'stale-while-revalidate' means in practice for visitors.
- Use on-demand revalidation to refresh a specific page immediately after a data change.
- Choose between time-based and on-demand revalidation for a given use case.
Core Definitions
- Incremental Static Regeneration (ISR): A rendering strategy where statically generated pages can automatically regenerate in the background after a specified time interval or an explicit trigger, without a full site rebuild.
- revalidate: A configuration option (either on a fetch call or a route segment) specifying, in seconds, the maximum time a statically generated page can be served before Next.js regenerates it in the background.
- Stale-while-revalidate: A caching pattern where a slightly outdated (stale) version of a page is served instantly to a visitor while a fresh version is generated in the background for future visitors.
- On-demand revalidation: Manually and immediately triggering the regeneration of a specific static page or set of pages, typically in response to a known data change, rather than waiting for a time interval to elapse.
- revalidatePath / revalidateTag: Functions used to trigger on-demand revalidation of specific paths or cache-tagged data, typically called from a Server Action or API route after a data mutation.
Detailed Explanation
The simplest form of ISR is time-based revalidation. By adding a revalidate option — either directly on a fetch call, like `fetch(url, { next: { revalidate: 3600 } })`, or exported from a page as `export const revalidate = 3600` — you tell Next.js that this page's static content can be served as-is for up to that many seconds (3600 seconds, or one hour, in this example) before it should be considered stale and eligible for regeneration.
Here's the key detail that makes ISR feel seamless: when a visitor requests a page whose revalidation window has expired, Next.js does NOT make them wait for a fresh render. Instead, it immediately serves the existing (now slightly stale) static version instantly, exactly as fast as before, while triggering a fresh regeneration of that page in the background. Once that regeneration completes, the newly built version replaces the old one, and the NEXT visitor after that point receives the updated content. This pattern is called stale-while-revalidate: no single visitor is ever blocked waiting for a rebuild; at worst, they see content that's marginally outdated by the length of one revalidation cycle.
Time-based revalidation works well when you have a general sense of how fresh content needs to be — a product price cache is fine to refresh hourly, for instance. But sometimes you know EXACTLY when data changes, because your own application caused that change. If an editor publishes a blog post update through a CMS, waiting up to an hour for the time-based revalidation window to naturally expire is unnecessary and frustrating. This is what on-demand revalidation solves: functions like revalidatePath('/blog/my-post') or revalidateTag('posts'), called from a Server Action or API route immediately after the underlying data changes, tell Next.js to regenerate that specific static content right now, rather than waiting for any time interval.
revalidatePath targets a specific URL path (or a whole path prefix), while revalidateTag targets any cached data associated with a particular tag, regardless of which pages that data appears on — useful when the same underlying data is rendered across multiple different pages, and a single mutation should refresh all of them at once.
Diagram Description
Visualize the stale-while-revalidate flow for time-based ISR:
[Page built at T=0, revalidate: 60]
[Visitor at T=30] --> served instantly, still 'fresh' (within 60s window)
[Visitor at T=75] --> served the EXISTING (stale) static page instantly
--> Next.js regenerates page in the BACKGROUND
--> new version now cached and ready
[Visitor at T=76] --> served the NEWLY regenerated, fresh page
On-demand revalidation flow:
[Editor updates blog post via CMS] --> [Server Action calls revalidatePath('/blog/my-post')]
--> [Static page regenerated immediately] --> [Very next visitor sees updated content]
Comparison Table
| Approach | Trigger | Freshness Guarantee | Typical Use Case |
|---|---|---|---|
| Time-based revalidate | N seconds have elapsed since last generation | At most N seconds stale | Product prices, dashboards updated periodically |
| revalidatePath | Manual call after a known data change | Immediate, exact | CMS content updates, admin edits |
| revalidateTag | Manual call targeting tagged data across pages | Immediate, exact, multi-page | Shared data (e.g., a global settings value) used on many pages |
Next.js Practical Example
// app/products/[id]/page.tsx — time-based ISR: regenerate at most every hour
async function getProduct(id: string) {
const res = await fetch(`https://api.example.com/products/${id}`, {
next: { revalidate: 3600, tags: ['products'] }, // refresh at most every 3600s
});
return res.json();
}
export default async function ProductPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
const product = await getProduct(id);
return <h1>{product.name} — ₹{product.price}</h1>;
}
// app/actions/updateProduct.ts — on-demand revalidation after an edit
'use server';
import { revalidateTag } from 'next/cache';
export async function updateProduct(id: string, newPrice: number) {
await fetch(`https://api.example.com/products/${id}`, {
method: 'PATCH',
body: JSON.stringify({ price: newPrice }),
});
revalidateTag('products'); // instantly refresh every page using 'products' tagged data
}
ProductPage fetches product data with `next: { revalidate: 3600, tags: ['products'] }`, meaning the static page will automatically regenerate in the background at most once per hour, following the stale-while-revalidate pattern — no visitor ever waits for a fresh render. The updateProduct Server Action demonstrates on-demand revalidation: after successfully updating a product's price via an API call, it immediately calls revalidateTag('products'), which instantly invalidates the cache for every page whose data fetch used the 'products' tag — potentially refreshing the product's individual page, a category listing page, and a homepage 'featured products' section all at once, without waiting for their individual hourly revalidation windows to expire.
Industry Examples
- E-commerce platforms use time-based ISR for product pages (prices refresh hourly) combined with on-demand revalidation via revalidateTag whenever an admin manually updates inventory or pricing.
- News websites use short time-based revalidation windows (e.g., 60 seconds) for homepage sections that need to feel near-live without the full server cost of SSR on every single request.
- CMS-powered marketing sites use on-demand revalidation triggered by a webhook from the CMS, so publishing or editing content in the CMS instantly refreshes the corresponding static Next.js page.
- Job listing platforms use ISR with moderate revalidation windows (e.g., every few minutes) to balance showing reasonably fresh listings against the cost of full SSR at high traffic volumes.
- SaaS status pages and public dashboards use short-interval time-based ISR to show near-real-time system health without the overhead of rendering fresh HTML on literally every visitor request.
Interview Questions and Answers
Q1. What problem does Incremental Static Regeneration solve?
ISR solves the staleness limitation of plain SSG (content fixed until a full rebuild) without requiring the full per-request server cost of SSR. It lets statically generated pages automatically refresh, either on a time interval or on-demand, while still being served as fast, cacheable static content most of the time.
Q2. What does the revalidate option control?
The revalidate option (set on a fetch call or exported from a route) specifies, in seconds, the maximum amount of time a statically generated page can be served before Next.js considers it stale and regenerates it in the background.
Q3. Explain the stale-while-revalidate pattern used by time-based ISR.
When a page's revalidation window has expired, Next.js immediately serves the existing, slightly stale static version to the current visitor with no delay, while regenerating a fresh version in the background. Once regeneration completes, subsequent visitors receive the updated content — no single visitor is ever blocked waiting for a rebuild.
Q4. What is the difference between revalidatePath and revalidateTag?
revalidatePath immediately invalidates and regenerates the cache for a specific URL path (or path prefix). revalidateTag invalidates any cached data associated with a specific tag, potentially affecting multiple different pages that all use data fetched with that same tag, refreshing all of them at once.
Q5. When would you choose on-demand revalidation over time-based revalidation?
On-demand revalidation is preferable when you know exactly when underlying data changes — such as an editor publishing a CMS update — since it refreshes content immediately rather than making visitors wait up to the full length of a time-based revalidation window for the change to appear.
MCQs With Answers
1. What does setting revalidate: 60 on a fetch call mean?
- The page will always render fresh, on every request
- The page can be served statically for at most 60 seconds before regenerating in the background
- The page will be deleted after 60 seconds
- The fetch will retry 60 times on failure
Answer: B. The page can be served statically for at most 60 seconds before regenerating in the background
Explanation: revalidate sets a time-based window; once it expires, the page is regenerated in the background following the stale-while-revalidate pattern.
2. In the stale-while-revalidate pattern, what does the visitor who triggers a stale check actually see?
- A loading spinner until the fresh page is ready
- The existing, slightly outdated static page, served instantly
- An error page
- A redirect to a different route
Answer: B. The existing, slightly outdated static page, served instantly
Explanation: The visitor is never blocked; they get the current cached version instantly while a fresh version regenerates in the background for future visitors.
3. Which function would you use to immediately refresh all pages using data tagged 'inventory'?
- revalidatePath('/inventory')
- revalidateTag('inventory')
- refreshCache('inventory')
- reloadPage()
Answer: B. revalidateTag('inventory')
Explanation: revalidateTag targets all cached data associated with a specific tag across potentially multiple pages, rather than a single specific path.
4. What is the main advantage of ISR over plain SSG?
- ISR requires no server at all
- ISR allows static pages to automatically refresh without a full manual site rebuild
- ISR eliminates the need for a database
- ISR only works with dynamic routes
Answer: B. ISR allows static pages to automatically refresh without a full manual site rebuild
Explanation: ISR blends SSG's speed with automatic freshness, either via a time interval or on-demand triggers, avoiding the need to rebuild and redeploy the entire site for every data change.
5. When is on-demand revalidation most appropriate?
- When you have no idea when data changes
- When you know exactly when underlying data changes, such as after a CMS publish action
- Only for pages with no dynamic content
- Only when using the Pages Router
Answer: B. When you know exactly when underlying data changes, such as after a CMS publish action
Explanation: On-demand revalidation lets you trigger an immediate refresh right when you know data has changed, rather than waiting for a time-based window to expire.
Common Mistakes to Avoid
- Assuming ISR means every visitor always sees perfectly fresh data — in reality, time-based ISR can serve slightly stale content for up to the revalidation window's duration.
- Forgetting to call revalidatePath or revalidateTag after a data mutation, resulting in visitors seeing outdated content until the next time-based window expires.
- Setting an extremely short revalidate value (like 1 second) unnecessarily, which increases regeneration frequency and server load without meaningful freshness benefit for most content.
- Confusing revalidatePath (path-specific) with revalidateTag (data-tag-specific, potentially multi-page) and using the wrong one for a given scenario.
- Not tagging fetch calls with `tags: [...]` when planning to use revalidateTag later, making tag-based on-demand revalidation impossible for that data.
Interview Notes
- ISR blends SSG's speed with automatic content freshness via time-based or on-demand revalidation.
- revalidate (in seconds) sets the maximum staleness window before a page regenerates in the background.
- Stale-while-revalidate: visitors during the regeneration window get the existing cached page instantly; the next visitor after regeneration completes gets the fresh version.
- revalidatePath targets a specific URL path; revalidateTag targets all cached data sharing a specific tag, potentially across multiple pages.
- On-demand revalidation is triggered manually, typically from a Server Action or API route, right after a known data change.
Key Takeaways
- ISR directly solves SSG's core weakness — staleness — without requiring the full per-request cost of SSR.
- Time-based revalidation is a simple, 'good enough freshness' approach; on-demand revalidation gives exact, immediate freshness when you control the data change.
- The stale-while-revalidate pattern ensures no visitor is ever blocked waiting for a page to regenerate.
- revalidateTag's ability to refresh multiple pages sharing the same underlying data at once makes it especially powerful for content that appears in several places across a site.
Summary
Incremental Static Regeneration blends the speed of Static Site Generation with the freshness of Server-Side Rendering, letting statically generated pages automatically refresh without requiring a full manual site rebuild. Time-based revalidation, set via a revalidate option in seconds, follows a stale-while-revalidate pattern: visitors are served the existing cached page instantly even after the window expires, while a fresh version regenerates in the background for subsequent visitors. On-demand revalidation, via revalidatePath or revalidateTag, provides exact, immediate freshness by triggering regeneration right when you know underlying data has changed — typically from a Server Action right after a data mutation. Together, these two approaches let teams choose between 'good enough, periodic freshness' and 'exact, instant freshness' depending on each page's specific needs.