Skip to content
Dashboard

ISR: A flexible way to cache dynamic content

Sr. Content Engineer

Effectively using ISR across industries and frameworks.

Link to headingBenefits of caching

Link to headingISR: A modern server-side caching strategy

Too many acronyms?

Learn the differences between rendering strategies like SSG, SSR, CSR, ISR, and PPR—and see real-world examples of each.

Read More

Link to headingISR and modern web architecture

A visual walkthrough of ISR.

Link to headingHow ISR fits in the caching stack

Ready to try ISR yourself?

This demo of on-demand ISR in Next.js shows you the basics by revalidating displayed GitHub issues from a webhook.

Deploy Now

Link to headingFramework adoption

Link to headingReal-world scenarios where ISR shines

We don't want to have to say to a business stakeholder, "Hey, that typo that you want fixed on the homepage? It's going to take us hours to get that out, even though everything's already approved. So, the ability to incrementally statically regenerate only certain pieces of content—particularly when you have 10,000 pages of content—is a very important driver for us when evaluating and picking out a platform.
Senior Software Engineer Johnson & Johnson

Link to headingEcommerce

Link to headingNews websites

Link to headingAI-powered applications

Link to headingHow to implement ISR

Link to headingTime-based ISR

src/routes/+page.server.ts
import { error } from "@sveltejs/kit";
import type { PageServerLoad } from "./$types";
export const load: PageServerLoad = async () => {
try {
const res = await fetch("<https://api.news.com/headlines>");
const headlines: any = await res.json();
return {
headlines,
// Revalidate every 10 minutes
cache: { maxage: 600 },
};
} catch (err) {
throw error(500, "Failed to fetch headlines");
}
};

Be careful when using time-based ISR at too frequent an interval (60 seconds, for example). Although ISR can provide a needed buffer in severe traffic to help you save costs, revalidating too often—writing to the cache almost as often as you're reading from it—can be cost-inefficient, and you may need to rethink your strategy.

Link to headingOn-demand ISR

astro.config.mjs
import { defineConfig } from "astro/config";
import vercel from "@astrojs/vercel/serverless";
export default defineConfig({
output: "server",
adapter: vercel({
isr: {
// Create a secret token for revalidation
bypassToken: import.meta.env.VERCEL_REVALIDATE_TOKEN,
},
}),
});

src/pages/posts/[slug].astro
---
// ... your existing code ...
export const prerender = true;
---
{/* ... your page content ... */}

curl -H "x-prerender-revalidate: <YOUR_BYPASS_TOKEN>" https://your-site.com/posts/my-article-slug

Link to headingTag-based ISR

app/products/[id]/page.tsx
import { revalidateTag } from "next/cache";
async function getProduct(id) {
// ... (fetch logic using tags: [`product-${id}`, "product-list"])
}
export default async function ProductPage({ params: { id } }) {
const product = await getProduct(id);
async function addToCart(formData) {
const quantityToAdd = Number(formData.get("quantity"));
if (quantityToAdd > 0 && quantityToAdd <= product.quantity) {
await updateProductQuantity(id, product.quantity - quantityToAdd);
revalidateTag(`product-${id}`);
revalidateTag("product-list");
// ... (add to cart logic)
}
}
return (
<div>
<h1>{product.name}</h1>
<p>Price: ${product.price}</p>
{product.quantity > 0 ? (
<form action={addToCart}>
<input
type="number"
name="quantity"
min="1"
max={product.quantity}
defaultValue="1"
/>
<button type="submit">Add to Cart</button>
</form>
) : (
<p>Out of Stock</p>
)}
</div>
);
}
// Server-side function to update quantity
async function updateProductQuantity(id, newQuantity) {
"use server";
// ... (update database logic)
}

Link to headingThe future of ISR

Link to headingTakeaways