Incremental Static Regeneration (ISR)
ISR (Incremental Static Regeneration) refreshes static pages in the background, with no full rebuild. How it works in Next.js and how to test it.
What is Incremental Static Regeneration (ISR)?
Incremental Static Regeneration (ISR) lets a site serve prerendered static pages and refresh them in the background after deployment, without rebuilding everything. When a page's cache expires, the next visitor still gets the stored copy while a new version is generated, and later visitors get the fresh one. ISR is a Next.js feature and works per page, not per site.
How ISR works
Take a blog with a route like /blog/[id]. The Next.js documentation describes this sequence:
- At
next build, Next.js prerenders a page for each id returned bygenerateStaticParams. - Requests to those pages are served from the cache and are very fast.
- After the revalidation time has passed (for example 60 seconds), the next request still receives the cached, now stale, page.
- That request also triggers regeneration of the page in the background.
- When the new version is generated successfully, it replaces the cached one and the next request gets it.
- A page that was not prerendered, such as
/blog/26, is generated on demand the first time it is requested if it exists. If it does not exist, the response is a 404. ThedynamicParamssetting changes this behavior.
This is the stale-while-revalidate pattern: speed first, freshness a moment later. If regeneration throws an error, the last successfully generated page keeps being served and Next.js retries on the next request.
Time-based and on-demand revalidation
Time-based revalidation gives a page a lifetime in seconds with the revalidate route segment option:
export const revalidate = 3600 // refresh at most once an hourOn-demand revalidation invalidates a page when the content really changes, for example after an editor publishes a post. In the App Router you call revalidatePath or revalidateTag from a Server Action or Route Handler:
'use server'
import { revalidatePath } from 'next/cache'
export async function createPost() {
// mark the /posts route as stale
revalidatePath('/posts')
}Both functions invalidate the cache entry, and regeneration happens on the next request, not immediately. The docs recommend a high time-based value, such as one hour instead of one second, and on-demand revalidation when you need more precision.
ISR with Cache Components
Next.js now documents two models, and the steps above describe ISR without Cache Components. When cacheComponents is enabled in your Next.js config, you cache with the use cache directive and give results a lifetime with cacheLife. ISR then works differently: for a URL that was not listed in generateStaticParams, Next.js serves a generic App Shell immediately and upgrades it in the background once the params are known. According to the docs, the App Shell for unlisted params is served from Next.js 16.3, and earlier versions wait for a full server render. The migration notes map getStaticProps with revalidate to use cache with cacheLife. Check which model your project uses before copying an example.
ISR compared with SSG and SSR
- Static site generation (SSG) builds every page once at build time, so content changes only when you rebuild.
- ISR starts from prerendered pages like SSG and refreshes them after deployment, on a timer or on demand.
- Server-side rendering (SSR) renders on every request. It is always fresh, but the server does the work each time unless a cache sits in front.
ISR fits content that changes, but not every second: product pages, articles, documentation. For per-user or real-time data, the Next.js docs point to dynamic rendering instead. Static output like this is also what a content delivery network (CDN) caches best. For the wider framework, see Next.js.
Common mistakes
- Expecting instant freshness. The first visitor after expiry sees the stale page. If that is not acceptable, use on-demand revalidation or dynamic rendering.
- Setting a very short revalidate value. One second regenerates the page constantly and defeats the point of static output.
- Running several instances on the default cache. The default file-system cache is per instance, so on-demand revalidation only invalidates the instance that receives the call. Use a shared custom cache handler.
- Using it where it is unsupported. ISR needs the Node.js runtime and is not supported with a static export.
- Revalidating the wrong path. Proxy does not run for on-demand ISR requests, so revalidate the exact path (
/post/1, not a rewritten/post-1). - Mixing fetch settings. With several fetches in one route, the lowest revalidate time wins, and a fetch with revalidate 0 or no-store makes the route dynamic.
Testing and monitoring ISR pages
A cached page and a page being regenerated behave very differently, so a test that only requests warm URLs measures the cache, not your server. Next.js adds an x-nextjs-cache response header with the values HIT, STALE, MISS or REVALIDATED, which tells you which path served a request. Record it in your checks.
In a load test, include URLs that were never prerendered or have just expired, because on-demand generation is where response times climb, and compare percentiles for cached and uncached URLs instead of one blended average. You can run that kind of test with LoadFocus load testing, and use page speed monitoring to track page speed on a schedule after deploys and content changes. For invalidation-specific traps, read cache invalidation and performance testing.
FAQ
What is the difference between ISR and SSG?
SSG builds pages once at build time. ISR starts from the same prerendered pages but can regenerate each one after deployment, on a timer or when you trigger it, so you never rebuild the whole site just to change one page.
Does the first visitor see old content?
Yes. When time-based revalidation has expired, the next request is served the stale cached page while Next.js regenerates it in the background. The visitor after that gets the new page.
Can I use ISR with a static export?
No. The Next.js docs state that ISR is not supported when creating a static export and that it needs the Node.js runtime.
Is ISR the same as Cache Components?
No, they are two caching models. With cacheComponents enabled you use the use cache directive and cacheLife, and ISR means serving an App Shell for unlisted URLs and upgrading it in the background. The route-level revalidate option belongs to the model without Cache Components.
How can I tell whether a page came from the cache?
Check the x-nextjs-cache response header. HIT means served from cache, STALE means served from cache while revalidating, MISS means rendered fresh, and REVALIDATED means regenerated through on-demand revalidation.
Related terms
Related LoadFocus Tools
Put this concept into practice with LoadFocus, the same platform that powers everything you just read about.