Implementing ISR in Next.js – Time-Based and On-Demand Static Page Regeneration

Incremental Static Regeneration (ISR) in Next.js allows you to update static pages without rebuilding the entire site. This article explains how to configure time-based revalidation, use revalidatePath and revalidateTag for on-demand updates, manage cache behavior, and troubleshoot ISR in production.

ISRrevalidaterevalidatePathrevalidateTag

~3 min read • Updated Oct 27, 2025

Introduction


Incremental Static Regeneration (ISR) in Next.js lets you update static content gradually without rebuilding your entire site. It improves performance, reduces build time, and ensures fresh data is served efficiently.


Basic Example


In app/blog/[id]/page.tsx, you can configure ISR like this:

export const revalidate = 60 // revalidate every 60 seconds

export async function generateStaticParams() {
  const posts = await fetch('https://api.vercel.app/blog').then(res => res.json())
  return posts.map(post => ({ id: String(post.id) }))
}

export default async function Page({ params }) {
  const { id } = await params
  const post = await fetch(`https://api.vercel.app/blog/${id}`).then(res => res.json())
  return (
    <main>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </main>
  )
}

Pages are generated at build time and revalidated in the background after 60 seconds.


Time-Based Revalidation


For example, on the /blog route:

export const revalidate = 3600 // revalidate every hour

export default async function Page() {
  const data = await fetch('https://api.vercel.app/blog')
  const posts = await data.json()
  return (
    <main>
      <h1>Blog Posts</h1>
      <ul>
        {posts.map(post => <li key={post.id}>{post.title}</li>)}
      </ul>
    </main>
  )
}

On-Demand Revalidation with revalidatePath


To revalidate a specific route after a mutation:

'use server'
import { revalidatePath } from 'next/cache'

export async function createPost() {
  revalidatePath('/posts')
}

On-Demand Revalidation with revalidateTag


To revalidate data tagged with posts:

fetch('https://api.vercel.app/blog', {
  next: { tags: ['posts'] }
})

Then:

'use server'
import { revalidateTag } from 'next/cache'

export async function createPost() {
  revalidateTag('posts')
}

Using unstable_cache


For database queries:

import { unstable_cache } from 'next/cache'

const getCachedPosts = unstable_cache(
  async () => await db.select().from(posts),
  ['posts'],
  { revalidate: 3600, tags: ['posts'] }
)

Error Handling


If revalidation fails, the last successful version is served. Next.js retries revalidation on the next request.


Custom Cache Location


You can configure cache storage to persist across deployments or share between containers.


Debugging in Development


To inspect cache behavior locally:

// next.config.js
module.exports = {
  logging: {
    fetches: {
      fullUrl: true,
    },
  },
}

// .env
NEXT_PRIVATE_DEBUG_CACHE=1

Caveats


  • ISR works only with the Node.js runtime
  • Not supported with Static Export
  • If any fetch uses revalidate: 0 or no-store, the route becomes dynamic
  • Proxy rewrites are not applied during on-demand ISR

Platform Support


Deployment OptionSupported
Node.js Server
Docker Container
Static Export
AdaptersPlatform-specific

Conclusion


ISR in Next.js enables efficient, scalable static page regeneration. With time-based and on-demand options, you can keep content fresh while maintaining performance and minimizing build time.


Written & researched by Dr. Shahin Siami