Skip to main content

Next.js Incremental Static Regeneration

In modern web development, balancing performance with content freshness presents a significant challenge. Next.js solves this with Incremental Static Regeneration (ISR), a powerful feature that provides the best of both static and dynamic rendering.

Introduction to Incremental Static Regeneration​

Incremental Static Regeneration (ISR) allows you to create or update static pages after you've deployed your site. This means you get the performance benefits of static generation while still keeping your content fresh.

Traditional static site generation has a significant limitation: when content changes, you need to rebuild the entire site to reflect those changes. This becomes problematic for sites with thousands of pages or frequently changing content.

ISR solves this by allowing you to:

  1. Generate static pages at build time (like regular Static Generation)
  2. Update specific pages in the background as traffic comes in
  3. Set a "staleness threshold" for your content

How ISR Works in Next.js​

ISR builds on Next.js's getStaticProps function by adding a revalidate property. Here's the basic pattern:

jsx
// pages/products/[id].js
export async function getStaticProps({ params }) {
const res = await fetch(`https://api.example.com/products/${params.id}`)
const product = await res.json()

return {
props: {
product,
},
// šŸ‘‡ This is where ISR magic happens
revalidate: 60, // In seconds
}
}

export async function getStaticPaths() {
return {
paths: [
{ params: { id: '1' } },
{ params: { id: '2' } }
],
fallback: 'blocking'
}
}

export default function Product({ product }) {
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
<p>Price: ${product.price}</p>
</div>
)
}

Let's break down how this works:

  1. The revalidate: 60 parameter tells Next.js this page can be regenerated at most once every 60 seconds
  2. The initial request after the revalidation period will still show the cached (stale) page
  3. Next.js triggers a regeneration of the page in the background
  4. Once regeneration completes, Next.js invalidates the cache and shows the updated page for subsequent requests

This gives you the best of both worlds: fast page loads with static content, plus automatic updates when your content changes!

The ISR Flow Explained​

To better understand the process, here's what happens with ISR:

  1. Initial Build: Your page is generated during the build process
  2. Deployment: The static page is deployed to a CDN for fast global access
  3. Stale Period: The page remains cached according to your revalidate time
  4. Revalidation Trigger: When a request comes in after the stale period
  5. Background Regeneration: Next.js generates a new version in the background
  6. Cache Invalidation: The new version replaces the old one in the cache

The key benefit is that users never see a loading state - they always get a rendered page instantly, even if it might be slightly out of date while a new version is being prepared.

Configuring ISR with Different Options​

Let's explore various ISR configurations to handle different use cases:

Basic ISR Implementation​

jsx
export async function getStaticProps() {
const res = await fetch('https://api.example.com/posts')
const posts = await res.json()

return {
props: {
posts
},
revalidate: 10, // Update at most every 10 seconds
}
}

Fallback Options with ISR​

When using ISR with dynamic routes, the fallback property in getStaticPaths becomes crucial:

jsx
// pages/posts/[slug].js
export async function getStaticPaths() {
// Get only the most popular posts at build time
const popular = await getPopularPosts()

return {
paths: popular.map(post => ({ params: { slug: post.slug } })),
fallback: 'blocking' // See options below
}
}

You have three options for fallback:

  1. false: Only pre-rendered paths will be served; others return 404
  2. true: Non-pre-rendered paths show a loading state while generating HTML
  3. 'blocking': Similar to server-side rendering; the user waits for the page to be generated without a loading state

For ISR, 'blocking' or true are typically the best choices.

Conditional Revalidation​

You can conditionally determine whether a page should be revalidated:

jsx
export async function getStaticProps() {
const res = await fetch('https://api.example.com/stats')
const data = await res.json()

// Only revalidate if data meets certain conditions
const shouldRevalidate = data.lastUpdated > Date.now() - (24 * 60 * 60 * 1000)

return {
props: { data },
revalidate: shouldRevalidate ? 60 : false // Only revalidate if data is recent
}
}

Real-World ISR Use Cases​

Let's explore some practical applications of ISR:

E-commerce Product Pages​

Product information like price, inventory, and details need to stay current:

jsx
// pages/products/[id].js
export async function getStaticProps({ params }) {
const product = await fetchProductById(params.id)

return {
props: { product },
// Update at most once every 5 minutes
// Fast enough for inventory changes but not too frequent
revalidate: 300,
}
}

News Site with Articles​

News content benefits from being statically generated for speed, yet needs to stay fresh:

jsx
// pages/news/[slug].js
export async function getStaticProps({ params }) {
const article = await fetchArticleBySlug(params.slug)
const relatedArticles = await fetchRelatedArticles(article.category)

return {
props: {
article,
relatedArticles,
lastFetched: new Date().toISOString()
},
// News updates frequently - check every minute
revalidate: 60,
}
}

Dashboard with Analytics​

For dashboards showing analytics that update periodically:

jsx
// pages/dashboard.js
export async function getStaticProps() {
const analytics = await fetchAnalyticsData()

return {
props: {
analytics,
lastUpdated: new Date().toISOString()
},
// Update every 30 minutes - analytics don't need real-time updates
revalidate: 30 * 60,
}
}

Handling ISR with On-Demand Revalidation​

Next.js 12.1 introduced On-Demand Incremental Static Regeneration which allows you to explicitly purge the cache and regenerate pages when needed:

jsx
// pages/api/revalidate.js
export default async function handler(req, res) {
// Check for secret to confirm this is a valid request
if (req.query.secret !== process.env.REVALIDATION_SECRET) {
return res.status(401).json({ message: 'Invalid token' })
}

try {
// Path to revalidate
const path = req.query.path

// This will trigger the regeneration
await res.revalidate(path)

return res.json({ revalidated: true })
} catch (err) {
// If there was an error, Next.js will continue
// to show the last successfully generated page
return res.status(500).send('Error revalidating')
}
}

You can trigger this API route when content changes:

bash
curl https://yourdomain.com/api/revalidate?secret=YOUR_SECRET_TOKEN&path=/products/123

This is perfect for:

  • CMS webhook integration
  • E-commerce inventory updates
  • Data changes from admin panels

Best Practices for ISR​

When implementing ISR in your Next.js applications, keep these best practices in mind:

  1. Choose appropriate revalidation times:

    • Too short: Unnecessary regeneration and API load
    • Too long: Stale content for users
  2. Use staggered revalidation for related pages to avoid server load spikes:

    jsx
    // Staggered revalidation example
    revalidate: 60 + Math.floor(Math.random() * 30) // 60-90 seconds
  3. Implement caching headers properly to work alongside ISR:

    jsx
    export async function getStaticProps() {
    // ...fetch data

    return {
    props: { data },
    revalidate: 60,
    headers: {
    'Cache-Control': 's-maxage=60, stale-while-revalidate'
    }
    }
    }
  4. Monitor your API usage as ISR still makes API calls during revalidation

  5. Combine with SWR or React Query for client-side updates between ISR regenerations:

    jsx
    import useSWR from 'swr'

    function ProductPage({ product: initialProduct }) {
    const { data: product } = useSWR(`/api/products/${initialProduct.id}`,
    fetcher,
    { fallbackData: initialProduct }
    )

    return <div>{product.name} - ${product.price}</div>
    }

When to Use (and Not Use) ISR​

Great Use Cases for ISR​

  • E-commerce product pages
  • Content-heavy websites (blogs, news sites)
  • Documentation sites
  • Portfolio and showcase websites
  • Marketing pages with occasionally changing content

When to Consider Alternatives​

  • Real-time data applications (chat, live sports scores)
  • User-specific content (authenticated dashboards)
  • Extremely high-frequency updates (stock tickers)
  • Pages where content must always be 100% current

Troubleshooting Common ISR Issues​

Page Not Updating After Revalidation Time​

Problem: Your page content stays stale even after the revalidation time has passed.

Solution:

  1. Verify the page is receiving traffic (no traffic = no revalidation trigger)
  2. Check if your data fetching logic has caching mechanisms
  3. Ensure you're not accidentally returning the same data each time

Memory or Performance Issues​

Problem: Your server is experiencing high memory usage with ISR.

Solution:

  • Increase revalidation times for less critical pages
  • Implement on-demand revalidation for important changes only
  • Consider serverless deployment platforms that handle scaling automatically

API Rate Limiting​

Problem: Your external API is rate-limiting due to frequent revalidations.

Solution:

  • Implement a caching layer in your data fetching
  • Increase revalidation periods
  • Batch your data fetching to minimize API calls

Summary​

Incremental Static Regeneration represents the perfect middle ground between fully static and fully dynamic pages. It offers:

  • Performance of static generation
  • Freshness of server-side rendering
  • Scalability without excessive rebuilds
  • Improved developer and user experience

By setting appropriate revalidation times and implementing on-demand revalidation when needed, you can create fast, dynamic websites that provide the best experience for your users while keeping your content fresh and relevant.

Additional Resources​

Practice Exercises​

  1. Create a blog with ISR where posts are regenerated every 5 minutes, but can be manually revalidated when the author updates content.

  2. Build a product listing page that uses ISR with a fallback strategy, displaying the most popular products at build time and generating others on demand.

  3. Implement an analytics dashboard that combines ISR (for base data that updates hourly) with client-side SWR for real-time metric updates.

  4. Create a news aggregator that uses different revalidation times based on the category of news (breaking news: 1 minute, regular news: 1 hour, archived content: 1 day).

By implementing these exercises, you'll gain a solid understanding of how to use ISR effectively in your Next.js applications!



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)