Get in Touch
Next.js Incremental Static Regeneration Explained: From Zero to Hero - Red Surge Technology Blog
Next.jsISRReactStatic GenerationBeginner

Imagine you run a bakery. Every morning, you bake fresh bread and put it on the shelf. Customers walk in, grab a loaf, and leave. That's static generation—fast, predictable, no waiting.

Now imagine your bakery gets a call: "Do you have gluten-free sourdough today?" You check the shelf. It's not there. So you stop what you're doing, run to the kitchen, and bake a fresh loaf on the spot. The customer waits five minutes, but they get exactly what they wanted. That's server-side rendering (SSR)—always fresh, but slower.

Incremental Static Regeneration (ISR) is like having an assistant who watches the shelf. When a loaf runs low, the assistant bakes a new one in the back, but customers can still buy the last few from the shelf without waiting. The shelf is never empty, and the customer never waits. That's the magic of ISR.

In Next.js terms, ISR lets you update static pages after they've been built, without a full rebuild. You get the speed of static generation with the freshness of server rendering. It's one of the most powerful features in modern web development, and it's easier to use than you might think.

The three ways Next.js renders a page

Before diving into ISR, let's quickly compare it to the other rendering strategies.

Static Site Generation (SSG) – The page is built once at deploy time. It's served as a static HTML file, which is lightning fast. But if your content changes, you need to rebuild the entire site to update that page. For a blog with thousands of posts, that's slow and wasteful.

Server-Side Rendering (SSR) – The page is rendered on every request. It's always up-to-date, but it's slower because the server must do work for every user. It also can't be cached by a CDN, so performance suffers under load.

Incremental Static Regeneration (ISR) – The page is generated statically at build time (or on first request), then re-generated in the background when the cached version becomes stale. Users always get a cached version instantly, and the server updates it when needed. It's the best of both worlds.

If you're already familiar with ISR vs SSR, you know the tradeoffs. This post is the beginner-friendly version, focused on building intuition.

How ISR actually works under the hood

Let's trace a user's request through an ISR-powered page.

  1. Build time: You have a page /blog/[slug]. At build, Next.js generates some pages statically (say, the top 10 posts) and caches them.

  2. User requests /blog/my-post: Next.js checks the cache. If the cached page is fresh (within the revalidation window), it serves it immediately. If it's stale, Next.js serves the stale page and triggers a background regeneration.

  3. Background regeneration: The server runs the page's data fetching code (e.g., fetch with revalidate), gets fresh data, and updates the cache. The user who triggered the regeneration still sees the old page, but the next user gets the new one.

This is called stale-while-revalidate. The user never waits for a rebuild. The cache is always available, even during updates. It's like the bakery assistant: the shelf never empties, and customers never see a "baking in progress" sign.

You set a revalidate interval—say, 60 seconds. That tells Next.js: "This page is fresh for 60 seconds. After that, if someone requests it, serve the stale copy and regenerate in the background." The interval can be as short as 1 second or as long as a year, depending on how often your content changes.

Your first ISR page: code example

In the App Router (Next.js 13+), ISR is achieved with the revalidate option on fetch or by exporting a revalidate constant from the page.

Let's create a simple product page that uses ISR.

// app/products/[id]/page.tsx
import { notFound } from 'next/navigation';

async function getProduct(id: string) {
  const res = await fetch(`https://api.example.com/products/${id}`, {
    next: { revalidate: 60 }, // Revalidate every 60 seconds
  });

  if (!res.ok) {
    notFound();
  }

  return res.json();
}

export default async function ProductPage({ params }: { params: { id: string } }) {
  const product = await getProduct(params.id);

  return (
    <div>
      <h1>{product.name}</h1>
      <p>{product.description}</p>
      <strong>${product.price}</strong>
    </div>
  );
}

That's it. The next: { revalidate: 60 } tells Next.js to cache the response and revalidate it every 60 seconds. The first user after that window triggers a background regeneration.

You can also set revalidation for the whole page by exporting a constant:

// app/products/[id]/page.tsx
export const revalidate = 60;

export default async function ProductPage({ params }) {
  // ...fetch data without per-fetch revalidate
}

If you're using the Pages Router (older Next.js), you'd use getStaticProps with a revalidate key:

export async function getStaticProps({ params }) {
  const product = await fetchProduct(params.id);
  return {
    props: { product },
    revalidate: 60, // 60 seconds
  };
}

On-demand revalidation: the manual override

Sometimes you don't want to wait for the timer. You want to update a page right now—for example, when a CMS webhook fires after a content update. That's on-demand revalidation.

You create an API route that calls revalidatePath or revalidateTag.

// app/api/revalidate/route.ts
import { revalidatePath } from 'next/cache';
import { NextRequest } from 'next/server';

export async function POST(request: NextRequest) {
  const { path } = await request.json();

  // Revalidate a specific path
  revalidatePath(path);

  return Response.json({ revalidated: true });
}

Then your CMS sends a webhook to this route when content changes. The page is regenerated immediately, and users see the update without waiting for the timer.

Tags are even more powerful. You can tag multiple pages with a category, like products, and revalidate all of them at once:

// In your fetch:
const res = await fetch("https://api.example.com/products", {
  next: { tags: ["products"] },
});

// In your revalidate route:
revalidateTag("products");

This is how you can keep large collections of pages in sync without rebuilding the whole site.

When should you use ISR?

ISR is perfect for content that changes occasionally but doesn't need to be fresh on every single request. Examples:

  • Blog posts and articles
  • Product pages (price, description, images)
  • Documentation pages
  • Marketing pages
  • E-commerce category pages
  • News articles (with short revalidation, like 1–5 minutes)

ISR is not suitable for:

  • User-specific dashboards (each user sees different data)
  • Real-time data (stock prices, live sports scores)
  • Checkout flows (must be accurate at the moment of purchase)
  • Any page that requires authentication or personalized content

For those, use SSR or client-side fetching.

If you've already read our Next.js ISR guide, you know the technical details. This post is the "explain it like I'm five" version, but the concepts are the same.

A real-world analogy that made it click

A developer on my team once described ISR as a "lazy susan for web pages." Static pages are like plates already on the table—fast, but if you want something new, the whole table must be reset. SSR is like cooking a new dish for every customer—fresh, but slow. ISR is like having a rotating tray where the kitchen refills dishes in the background while you keep eating what's already served.

That analogy stuck. ISR doesn't make your code more complicated; it just changes when the cooking happens. You still write the same data fetching logic. You still render the same React components. You just tell Next.js, "This page is good for 60 seconds, then refresh it in the background."

Common pitfalls for beginners

1. Forgetting that revalidation is asynchronous. When you trigger an ISR update, the user who triggered it still sees the old content. The next user sees the fresh version. Don't expect instant updates unless you use on-demand revalidation.

2. Setting revalidation too low. A revalidation interval of 1 second means your server is regenerating pages constantly, which can defeat the purpose of static caching. Be realistic about how often your data actually changes.

3. Using ISR for personalized content. If your page shows different data to different users (like account info), ISR will serve the same cached page to everyone. That's a recipe for data leaks. Use SSR or client-side fetching instead.

4. Not handling regeneration failures. If the API call fails during regeneration, Next.js will keep serving the stale page. That's a feature, not a bug, but you should log the error and set up monitoring so you know when updates are failing.

5. Confusing revalidate with no-store. revalidate: 0 means "never cache"—but that's different from cache: 'no-store'. With ISR, you always want a positive revalidation window unless you're explicitly using SSR.

Is ISR the future of web development?

The web is moving toward a hybrid model: static where possible, dynamic where necessary. ISR embodies that philosophy perfectly. It lets you start with a static site and gradually add dynamic behavior as needed, without rewriting your architecture.

Frameworks like Next.js have made ISR accessible to every developer, not just performance specialists. The benefits are concrete: faster page loads, lower server costs, and better SEO. Users get a snappy experience, and developers get a maintainable codebase.

If you're building a content-heavy site and still using SSR everywhere, give ISR a try. You might be surprised how easy it is and how much it improves performance.

Wrapping up

Incremental Static Regeneration is the sweet spot between static generation and server-side rendering. You get the speed of static with the freshness of dynamic. And with the App Router, it's just a revalidate property on your fetch call.

Start with a long revalidation interval (e.g., 3600 seconds) for stable content. Move to shorter intervals (e.g., 60 seconds) for frequently updated content. Use on-demand revalidation for immediate updates. And always monitor regeneration failures to ensure your content stays fresh.

ISR is one of those features that, once you understand it, changes how you think about web architecture. No more rebuilding the whole site just to fix a typo. No more waiting for SSR on every request. Just fast, fresh, and simple.

For a deeper technical dive, check out our Next.js ISR guide and our comparison of ISR vs SSR. They cover advanced patterns like on-demand tags, error handling, and deployment considerations.

Now go bake some bread—or at least, let your assistant do it in the background.


Ready to implement ISR in your Next.js project? Red Surge Technology helps teams design content architectures that are both fast and fresh. Get in touch to discuss your project.