Getting started with ISR
This guide helps you set up Incremental Static Regeneration (ISR) with your Vercel project. With ISR, you can regenerate pages without rebuilding and redeploying your site. When a page with ISR enabled regenerates, Vercel fetches the most recent data and updates the cache. There are two ways to trigger regeneration:
- Time-based revalidation: Regeneration that recurs automatically at a set interval
- On-demand revalidation: Regeneration that you trigger explicitly through an API call
You also control when pages are first cached:
- Pre-render at build time: Generate pages during the build so the first visitor gets an instant cache hit. This increases build time but avoids slow first requests.
- Generate on first request: Skip the build step and let the first visitor trigger generation at runtime. This keeps builds fast but means the first request for each page is slower (a cache miss).
A common pattern is to pre-render popular pages at build time and let the rest generate on demand.
- A project deployed on Vercel
- A supported framework: Next.js, SvelteKit, Nuxt, Astro, Gatsby, or a custom solution using the Build Output API
| Framework | ISR support | On-demand revalidation |
|---|---|---|
| Next.js (App Router) | revalidate route segment config | revalidatePath / revalidateTag |
| Next.js (Pages Router) | revalidate in getStaticProps | res.revalidate API route |
| SvelteKit | config.isr export | x-prerender-revalidate header |
| Nuxt | routeRules with isr option | x-prerender-revalidate header |
| Astro | Server output with ISR config | Framework-specific |
| Gatsby | Deferred Static Generation (DSG) | Framework-specific |
Time-based revalidation purges the cache for an ISR route automatically at a set interval. When the interval elapses and a visitor requests the page, Vercel serves the stale version and regenerates the page in the background.
When using Next.js with the App Router, you can enable ISR by using the revalidate route segment config for a layout or page.
export const revalidate = 10; // secondsThe following example renders a list of blog posts from a demo API, revalidating every 10 seconds:
export async function getStaticProps() {
const res = await fetch('https://api.vercel.app/blog');
const posts = await res.json();
return {
props: {
posts,
},
revalidate: 10,
};
}
interface Post {
title: string;
id: number;
}
export default function BlogPosts({ posts }: { posts: Post[] }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}export const revalidate = 10; // seconds
interface Post {
title: string;
id: number;
}
export default async function Page() {
const res = await fetch('https://api.vercel.app/blog');
const posts = (await res.json()) as Post[];
return (
<ul>
{posts.map((post: Post) => {
return <li key={post.id}>{post.title}</li>;
})}
</ul>
);
}To test this code, run the appropriate dev command for your framework and navigate to the /blog-posts/ route.
You should see a bulleted list of blog posts.
On-demand revalidation lets you purge the cache for an ISR route at any time, without waiting for a time interval to elapse. This is useful when your content changes based on external events, such as a CMS publish or a webhook.
Tag-based revalidation is the recommended approach for granular control. Instead of revalidating entire paths, you tag cached content and invalidate specific tags when the underlying data changes.
To revalidate a page on demand with Next.js:
- Create an Environment Variable which will store a revalidation secret
- Create an API Route that checks for the secret, then triggers revalidation
The following example demonstrates an API route that triggers revalidation if the query paramater ?secret matches a secret Environment Variable:
export default async function handler(request, response) {
// Check for secret to confirm this is a valid request
if (request.query.secret !== process.env.MY_SECRET_TOKEN) {
return response.status(401).json({ message: 'Invalid token' });
}
try {
// This should be the actual path, not a rewritten path
// e.g. for "/blog-posts/[slug]" this should be "/blog-posts/1"
await response.revalidate('/blog-posts');
return response.json({ revalidated: true });
} catch (err) {
// If there was an error, Next.js will continue
// to show the last successfully generated page
return response.status(500).send('Error revalidating');
}
}import type { NextApiRequest, NextApiResponse } from 'next';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
// Check for secret to confirm this is a valid request
if (req.query.secret !== process.env.MY_SECRET_TOKEN) {
return res.status(401).json({ message: 'Invalid token' });
}
try {
// This should be the actual path, not a rewritten path
// e.g. for "/blog-posts/[slug]" this should be "/blog-posts/1"
await res.revalidate('/blog-posts');
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');
}
}import { revalidatePath } from 'next/cache';
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
if (searchParams.get('secret') !== process.env.MY_SECRET_TOKEN) {
return new Response('Invalid credentials', {
status: 401,
});
}
revalidatePath('/blog-posts');
return Response.json({
revalidated: true,
now: Date.now(),
});
}import { revalidatePath } from 'next/cache';
export async function GET(request) {
const { searchParams } = new URL(request.url);
if (searchParams.get('secret') !== process.env.MY_SECRET_TOKEN) {
return new Response('Invalid credentials', {
status: 401,
});
}
revalidatePath('/blog-posts');
return Response.json({
revalidated: true,
now: Date.now(),
});
}See the time-based revalidation section above for a full ISR example.
- How ISR works: Understand the request flow from build time through revalidation
- Caching on Vercel: Compare ISR with other caching strategies
- ISR usage and pricing: Understand costs and optimization strategies
- Monitor ISR on Vercel: Track ISR performance in your dashboard
Was this helpful?


