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:
- Generate static pages at build time (like regular Static Generation)
- Update specific pages in the background as traffic comes in
- 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:
// 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:
- The
revalidate: 60
parameter tells Next.js this page can be regenerated at most once every 60 seconds - The initial request after the revalidation period will still show the cached (stale) page
- Next.js triggers a regeneration of the page in the background
- 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:
- Initial Build: Your page is generated during the build process
- Deployment: The static page is deployed to a CDN for fast global access
- Stale Period: The page remains cached according to your
revalidate
time - Revalidation Trigger: When a request comes in after the stale period
- Background Regeneration: Next.js generates a new version in the background
- 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ā
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:
// 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
:
false
: Only pre-rendered paths will be served; others return 404true
: Non-pre-rendered paths show a loading state while generating HTML'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:
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:
// 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:
// 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:
// 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:
// 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:
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:
-
Choose appropriate revalidation times:
- Too short: Unnecessary regeneration and API load
- Too long: Stale content for users
-
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 -
Implement caching headers properly to work alongside ISR:
jsxexport async function getStaticProps() {
// ...fetch data
return {
props: { data },
revalidate: 60,
headers: {
'Cache-Control': 's-maxage=60, stale-while-revalidate'
}
}
} -
Monitor your API usage as ISR still makes API calls during revalidation
-
Combine with SWR or React Query for client-side updates between ISR regenerations:
jsximport 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:
- Verify the page is receiving traffic (no traffic = no revalidation trigger)
- Check if your data fetching logic has caching mechanisms
- 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ā
-
Create a blog with ISR where posts are regenerated every 5 minutes, but can be manually revalidated when the author updates content.
-
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.
-
Implement an analytics dashboard that combines ISR (for base data that updates hourly) with client-side SWR for real-time metric updates.
-
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! :)