Incremental Static Regeneration (ISR) lets you get the performance of static rendering while still updating content periodically, without a full rebuild and redeploy. This lesson covers time-based and on-demand revalidation.
Static Pages That Update Themselves
ISR works by statically rendering a route (or a specific fetch call within it) and attaching an expiration window. The first request after that window expires still receives the existing cached HTML instantly, but Next.js regenerates the page in the background and swaps in the fresh version for subsequent requests — visitors never wait on a slow regeneration.
This makes ISR ideal for content that changes occasionally but doesn't need to be instantaneous — product listings, blog indexes, or marketing pages backed by a CMS that gets updated a few times a day.
// app/products/page.tsx
export default async function ProductsPage() {
const products = await fetch("https://api.example.com/products", {
next: { revalidate: 300 }, // regenerate at most every 5 minutes
}).then((r) => r.json());
return <ul>{products.map((p: any) => <li key={p.id}>{p.name}</li>)}</ul>;
}
For the first five minutes after generation, every visitor gets the same cached HTML instantly; after that, the next request triggers a background regeneration.
next: { revalidate: N } on a specific fetch() call sets that call's staleness window in seconds.
export const revalidate = N at the top of a page sets a default revalidation window for the whole route.
A revalidate value of 0 behaves like no-store (always dynamic); false means "cache forever, no automatic revalidation".
Time-based revalidation happens lazily — it triggers on the next request after expiration, not on a background timer.
ISR Cheat Sheet
Configuration options and what they mean.
Setting
Effect
revalidate: 60
Regenerate at most once every 60 seconds
revalidate: false
Cache indefinitely, no automatic revalidation
revalidate: 0
Effectively disables ISR for that fetch (always dynamic)
revalidateTag("tag")
Invalidate cached data on demand, from a Server Action or Route Handler
revalidatePath("/products")
Invalidate a specific route's cache on demand
On-Demand Revalidation
Time-based revalidation is convenient but imprecise — content might update up to N seconds late. On-demand revalidation solves this by letting your own code (typically a Server Action or webhook-driven Route Handler) explicitly invalidate cached content the moment it actually changes.
The /products page regenerates immediately after this action runs, instead of waiting for its time-based window to expire.
The Stale-While-Revalidate Model
ISR follows a "stale-while-revalidate" pattern: visitors are never blocked waiting for regeneration. The currently cached (possibly slightly stale) version is served instantly while a fresh version is generated in the background for future requests.
No visitor ever experiences the regeneration delay directly.
At most one "stale" version is ever served after the revalidation window passes.
Regeneration failures fall back to serving the last successfully generated version.
Common Mistakes
Expecting content to update the instant the revalidation window expires, rather than on the next request after that.
Setting a very short revalidate value for expensive-to-generate pages, causing frequent, costly regenerations.
Forgetting revalidateTag()/revalidatePath() exist for cases where waiting on a timer isn't precise enough.
Confusing revalidate: false (cache forever) with revalidate: 0 (effectively no caching).
Key Takeaways
ISR combines static performance with periodic content freshness.
next: { revalidate } and export const revalidate both configure time-based revalidation.
Visitors always get an instant response — regeneration happens in the background, never blocking a request.
revalidateTag() and revalidatePath() enable precise, on-demand revalidation.
ISR is ideal for content that changes occasionally but doesn't require instant, per-request freshness.
Pro Tip
Pair a generous time-based revalidate window (as a safety net) with on-demand revalidatePath()/revalidateTag() calls triggered right after content actually changes — this gives you both guaranteed eventual freshness and near-instant updates when you know exactly when data changed.
You've completed the data-fetching and rendering fundamentals. Next, learn how to build backend API endpoints directly inside your Next.js app with API Routes.