What Is ISR — Incremental Static Regeneration Explained
In this tutorial, you will learn about What Is ISR. We cover key concepts, practical examples, and best practices to help you master this topic.
Incremental Static Regeneration updates static pages after build by re-rendering individual pages in the background, combining SSG speed with fresh content.
What You'll Learn
By the end of this tutorial, you'll understand what ISR is, how it bridges SSG and SSR, the stale-while-revalidate pattern, and when to use ISR over other rendering strategies.
Why It Matters
Traditional SSG requires a full rebuild to update content. For large sites this takes hours. ISR updates only the pages that changed, keeping your site fast and fresh without sacrificing the performance benefits of static generation.
Real-World Use
An e-commerce site with 50,000 products uses ISR to update prices and inventory. When a price changes, only that product page regenerates. The rest of the site stays cached and fast, while the updated page is available within seconds.
ISR Architecture
graph LR
A[Build Time] --> B[Pre-render pages
static HTML]
A --> C[Set revalidate
time window]
B --> D[CDN Cache
serves static HTML]
C --> D
D --> E[First visit after
revalidate window]
E --> F[Serve stale page
immediately]
E --> G[Background re-render]
G --> H[New static HTML
replaces cache]
H --> D
style B fill:#27ae60,color:#fff
style F fill:#e67e22,color:#fff
style G fill:#4a90d9,color:#fff
style H fill:#27ae60,color:#fff
Basic ISR Implementation
// pages/posts/[slug].js — ISR with time-based revalidation
export default function Post({ post, generatedAt }) {
return (
<article>
<h1>{post.title}</h1>
<p className="meta">
Last generated: {new Date(generatedAt).toLocaleString()}
</p>
<div>{post.content}</div>
</article>
);
}
export async function getStaticPaths() {
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();
return {
paths: posts.map(post => ({
params: { slug: post.slug }
})),
fallback: 'blocking'
};
}
export async function getStaticProps({ params }) {
const res = await fetch(`https://api.example.com/posts/${params.slug}`);
const post = await res.json();
return {
props: {
post,
generatedAt: Date.now()
},
// Revalidate every 60 seconds
revalidate: 60
};
}
How Stale-While-Revalidate Works
// ISR lifecycle visualization
const isrLifecycle = {
step1: 'Build: Page is pre-rendered as static HTML',
step2: 'Cache: Static file stored on CDN',
step3: 'Request: User visits the page',
step4: 'Check: Has revalidate window passed?',
step5: 'No: Serve cached page directly (instant)',
step6: 'Yes: Serve stale page immediately + trigger re-render',
step7: 'Render: Server generates fresh HTML in background',
step8: 'Update: New HTML replaces cache for next visitor'
};
// Visual representation
const staleTime = {
served: 'Stale page (maybe seconds or minutes old)',
reason: 'User gets instant response, no waiting',
background: 'Fresh page being generated silently',
nextUser: 'Gets the brand new page'
};
ISR with External Data
// pages/products/[id].js — E-commerce ISR
export async function getStaticProps({ params }) {
const { id } = params;
// Fetch product data
const productRes = await fetch(`https://api.example.com/products/${id}`);
if (!productRes.ok) {
return { notFound: true };
}
const product = await productRes.json();
// Also fetch inventory
const inventoryRes = await fetch(
`https://api.example.com/inventory/${id}`
);
const inventory = await inventoryRes.json();
return {
props: {
product: {
...product,
inStock: inventory.quantity > 0,
stockCount: inventory.quantity
},
generatedAt: Date.now()
},
// Dynamic revalidate based on stock status
revalidate: product.inStock ? 60 : 300
};
}
Common Mistakes
- Setting revalidate too low. Revalidate: 1 means every Visitor triggers a re-render. Use 60+ seconds for most content or use on-demand revalidation.
- Not using fallback: 'blocking'. Without fallback, uncached pages return 404. 'blocking' generates them on first request and caches the result.
- Confusing revalidate with cache headers. revalidate controls ISR. Cache-Control headers control CDN Caching. Both are needed for correct behavior.
- Assuming ISR works without a server. ISR requires a persistent server runtime. Static export (output: 'export') doesn't support ISR.
- Not handling revalidation errors. If the re-render fails, stale content persists indefinitely. Monitor revalidation health.
Practice Questions
- What problem does ISR solve that SSG doesn't address?
- How does the stale-while-revalidate pattern work?
- What does the revalidate property control in getStaticProps?
- What happens during the background revalidation step?
- Why does ISR require a server runtime?
Challenge: Build a product catalog with ISR: implement time-based revalidation (120s), display the generation timestamp on each page, verify that stale content serves instantly while fresh content generates in the background, and measure the timing difference.
FAQ
Mini Project
Create an ISR-powered news ticker: build a page that displays current headlines with revalidate: 30, verify that the page updates automatically, show the last-generated timestamp, and compare the response time of ISR vs SSR for the same content.
What's Next
You understand ISR basics. Now compare ISR vs SSG vs SSR to know exactly when each rendering Strategy is appropriate.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro