Incremental Static Regeneration in Next.js, Explained
ISR gives you static-page speed with content that stays fresh. Here is how it works under the bonnet, how to trigger it on demand, and the pitfalls worth knowing.
Static pages are fast and cheap to serve, but content changes. Rebuilding an entire site because someone fixed a typo does not scale past a few hundred pages. Incremental Static Regeneration is the compromise Next.js offers: pages are generated statically, then regenerated individually — either on a timer or when you explicitly ask.
How ISR actually works
ISR follows the stale-while-revalidate model. When a request arrives after the revalidation window has passed, the visitor still receives the cached page instantly. In the background, Next.js regenerates the page, and the next visitor gets the fresh version. Nobody waits for a rebuild; the cost of regeneration is paid off the critical path.
The consequence worth internalising is that ISR never guarantees freshness — it guarantees a ceiling on staleness. If a page revalidates every sixty seconds, a visitor may see content up to a minute old, plus one request to trigger the refresh. For most marketing and content pages that trade-off is exactly right.
Time-based revalidation
The simplest form is a route-level export. Every fetch on the page inherits the interval unless it sets its own, and the page as a whole becomes eligible for regeneration once the window lapses.
// app/blog/[slug]/page.tsx
export const revalidate = 3600; // seconds
export default async function BlogPost({ params }: Props) {
const { slug } = await params;
const post = await getPost(slug);
return <Article post={post} />;
}Choosing the window is a judgement about consequence, not technology. Ask what happens if a visitor sees an hour-old version of this page. For a blog post, nothing; for a price, possibly a mis-sold order. We commonly run marketing pages at an hour or more, listings at a few minutes, and anything transactional fully dynamic.
On-demand revalidation
Timers are a blunt instrument. If content only changes when an editor hits publish, regenerate only then. Tag your fetches, then call revalidateTag from a Server Action or a webhook-triggered Route Handler when the CMS notifies you of a change.
// app/api/revalidate/route.ts
import { revalidateTag } from 'next/cache';
export async function POST(request: Request) {
const body = await request.json();
if (body.secret !== process.env.REVALIDATE_SECRET) {
return Response.json({ ok: false }, { status: 401 });
}
revalidateTag('posts');
return Response.json({ ok: true });
}This combination — long or infinite revalidation windows plus on-demand invalidation — is the pattern we deploy for almost every content site. Pages stay static indefinitely and update within seconds of publishing.
The webhook route above is deliberately boring: check a shared secret, invalidate a tag, return. Resist the urge to make it cleverer — revalidation endpoints run unattended for years, and boring is exactly what you want from them.
New paths and generateStaticParams
ISR also covers pages that did not exist at build time. With generateStaticParams you prerender the paths you know about; requests for unknown slugs are rendered on demand, cached, and served statically from then on. Set dynamicParams to false only when you genuinely want unknown paths to 404.
Pitfalls worth knowing
- Self-hosted deployments share the ISR cache via the filesystem — multiple instances need a shared cache handler or a single writer
- revalidatePath and revalidateTag invalidate the cache; regeneration happens on the next request, not immediately
- A revalidation that throws keeps serving the last good page, which is a feature, but watch your error logs or you will not notice
- Remember the client-side Router Cache: users navigating within the app may briefly see cached views after invalidation
ISR is one of the strongest reasons to choose Next.js for content-heavy sites. Treat revalidation as part of your content model, not an afterthought, and it will quietly do the right thing for years.
Need help getting caching and revalidation right on a production Next.js site? That is the sort of problem STRCLI enjoys — say hello.