NextjsIncremental Static Regeneration (ISR)

Incremental Static Regeneration (ISR)

Static Site Generation gives you the speed of pre-rendered HTML, but a purely static page can go stale the moment your data changes. Incremental Static Regeneration (ISR) solves this by letting Next.js regenerate a statically rendered page in the background, after it has already been served to users, without you having to rebuild and redeploy the whole site.

With ISR, a page is generated at build time (or on first request) just like a normal static page. Then, after a time interval you define, or in response to an explicit signal from your code, Next.js quietly regenerates that page behind the scenes and swaps in the fresh version — all while continuing to serve fast, cached HTML to visitors in the meantime.

Enabling ISR with revalidate

The simplest way to opt a page into ISR is to add a revalidate export to a route segment, or pass a next.revalidate option to fetch. Either tells Next.js how many seconds a cached version of the page is allowed to live before it becomes eligible for regeneration.

TSX
// app/[locale]/blog/[slug]/page.tsx

// Revalidate this route at most every 60 seconds
export const revalidate = 60

async function getPost(slug: string) {
  const res = await fetch(`https://api.example.com/posts/${slug}`)
  return res.json()
}

export default async function PostPage({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug)

  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.body}</p>
    </article>
  )
}

The same behavior can be scoped to an individual fetch call instead of the whole route, which is useful when a page combines data that changes at different rates.

TSX
async function getPost(slug: string) {
  const res = await fetch(`https://api.example.com/posts/${slug}`, {
    next: { revalidate: 60 },
  })
  return res.json()
}
Stale-while-revalidate behavior

ISR follows a stale-while-revalidate model, the same idea used by HTTP caching for decades:

  1. A request comes in before the revalidate window has elapsed. Next.js serves the cached page immediately — no regeneration happens.

  2. A request comes in after the revalidate window has elapsed. Next.js still serves the existing (now "stale") cached page immediately, so the visitor never waits.

  3. In the background, Next.js triggers a regeneration of that page.

  4. Once regeneration succeeds, Next.js invalidates the old cache and swaps in the newly generated page for subsequent requests.

  5. If regeneration fails, the previously cached page keeps being served, and Next.js retries on a later request.

Note
No visitor is ever blocked waiting for a rebuild. At worst, a user sees data that is up to revalidate seconds old, and only the request that happens to trigger the background regeneration is involved in kicking it off — it still gets the fast, stale response.
ISR vs. fully static vs. fully dynamic

Strategy

When HTML is generated

Freshness

Typical use case

Static (no revalidate)

Build time only

Never updates without a redeploy

Legal pages, marketing pages

ISR (revalidate: N)

Build time, then regenerated in the background

Eventually consistent, up to N seconds stale

Blog posts, product pages, dashboards

Dynamic (force-dynamic / no-store)

On every request

Always fresh

User-specific or highly volatile data

On-demand revalidation

Time-based revalidation is a blunt instrument — it regenerates on a schedule whether or not anything actually changed. When you know exactly when data changed (for example, right after an editor saves a blog post), you can trigger regeneration immediately and precisely with revalidatePath or revalidateTag, typically called from a Server Action or a Route Handler.

TSX
'use server'

import { revalidatePath } from 'next/cache'

export async function publishPost(slug: string) {
  await savePostToDatabase(slug)

  // Force Next.js to regenerate this exact path right now
  revalidatePath(`/blog/${slug}`)
}
Tip
Time-based revalidate is a good default safety net; on-demand revalidation with revalidatePath and revalidateTag is the precise tool for "I know this changed right now." The two dedicated pages in this section cover them in depth.
Things to watch for
  • revalidate is a route-segment config — it applies to the whole page/layout, not just one fetch call.

  • If a page has multiple revalidate values across nested layouts and pages, Next.js uses the lowest (most frequent) one.

  • Setting revalidate = 0 or using cache: "no-store" opts a route out of static caching entirely, making it fully dynamic.

  • ISR works the same way whether you deploy to a Node.js server or to a platform with edge/serverless caching support — the underlying primitive is the same.

Warning
ISR regenerates pages that were already generated at build time or previously requested. If a path was never generated (and is not covered by generateStaticParams), the very first request to it triggers a synchronous, on-demand static generation rather than an instant cache hit.