Choosing a Content Update Strategy for Jamstack Sites: ISR, On-Demand Revalidation, or Full Rebuilds
A decision guide comparing ISR, On-Demand Revalidation, and Full Static Regeneration for Jamstack sites. Includes constraints, trade-off table, and concrete Next.js 14 implementations for Vercel with validation steps.
26 Jul 2025, 07:45 UTC

The Decision You're Facing
Your Jamstack site needs content updates without a full redeploy every time marketing changes a headline. You have three main paths: Incremental Static Regeneration (ISR), On-Demand Revalidation, or Full Static Regeneration. Each trades off freshness, build cost, operational complexity, and platform lock-in differently. This guide helps you pick based on your page count, update frequency, team capacity, and hosting provider.
Constraints That Shape the Choice
- Page count: Under 1,000 pages? Full rebuilds are viable. 1,000–50,000? ISR or on-demand becomes necessary. 50,000+? You need on-demand with selective invalidation.
- Update latency tolerance: Can stakeholders wait 60 seconds (ISR default) or do they need "publish and see it now" (on-demand)?
- Build minute budget: Free tiers on Vercel/Netlify cap at 100–300 build minutes/month. A 5,000-page rebuild at 2 min each burns your quota in one deploy.
- Team ops capacity: On-demand requires webhook security, monitoring, and retry logic. ISR is mostly set-and-forget.
- Hosting platform: Vercel has native ISR. Netlify needs Edge Functions (beta). Cloudflare Pages uses Functions + KV. AWS Amplify uses Lambda@Edge. Your provider may decide for you.
Quick Comparison
| Strategy | Freshness | Build Cost | Ops Complexity | Best For | Platform Support |
|---|---|---|---|---|---|
ISR (revalidate: 60) | Stale up to revalidate seconds | Per-page, on demand | Low | High-traffic pages, predictable update cadence | Vercel (native), Netlify (Edge Functions), Cloudflare (Functions+KV), Amplify (Lambda@Edge) |
| On-Demand Revalidation | Near-real-time (seconds) | Per-page, triggered | High (webhooks, secrets, retries) | Low-latency needs, event-driven CMS | Next.js 13+ (App Router), Astro (adapters), Remix (limited) |
| Full Static Regeneration | Instant at deploy | All pages, every change | Lowest | Small sites (<1k pages), infrequent updates | All static hosts (Netlify, Vercel, Cloudflare, GitHub Pages) |
Trade-offs in Practice
ISR: The Stale-While-Revalidate Window
ISR serves the cached page immediately while triggering a background regeneration. A visitor hitting the page during the revalidate window sees stale content. Set revalidate too low (e.g., 10 seconds) and you hammer your origin CMS. Too high (3600) and marketing complains about delays. A practical starting point: revalidate: 60 for marketing pages, revalidate: 300 for blog posts.
Limitation: ISR doesn't purge the CDN cache globally on all providers. Vercel handles this; Netlify Edge Functions may serve stale responses from edge nodes until TTL expires. Verify your provider's cache invalidation behavior.
On-Demand: Webhook Security and Cost
On-demand revalidation (Next.js revalidatePath or revalidateTag) gives you "publish and it's live" but you own the pipeline: CMS webhook → your API route → framework revalidation → CDN purge. You must:
- Sign webhooks with HMAC secrets (Contentful, Sanity, Strapi all support this)
- Handle retries with idempotency keys
- Monitor invocation counts—serverless functions cost money at scale
- Rate-limit to prevent CMS loops triggering rebuild storms
Risk: A misconfigured webhook secret or a CMS retry storm can exhaust your function quota or DDOS your origin.
Full Rebuild: The Queue Trap
Simple mental model: change content → trigger build hook → wait → deploy. But at 5,000 pages with 2-minute builds, you hit Netlify's 15-minute free-tier limit. Vercel's 45-minute limit buys more room. Queue times during peak hours add 5–20 minutes. If marketing publishes 10 times a day, you're always building.
When it works: Documentation sites updated weekly, personal blogs, landing pages under 500 pages.
Concrete Implementation: Next.js 14 ISR on Vercel
This example shows a product page using ISR with a 60-second revalidation window. Run these steps in a new or existing Next.js 14 project (App Router).
1. Create the Page with getStaticProps Equivalent
// app/products/[slug]/page.tsx
import { Metadata } from 'next'
interface Product {
slug: string
name: string
description: string
price: number
updatedAt: string
}
export async function generateStaticParams() {
// Fetch all slugs at build time
const res = await fetch(`${process.env.CMS_API}/products?fields=slug`, {
headers: { 'Authorization': `Bearer ${process.env.CMS_TOKEN}` },
next: { revalidate: 3600 } // refresh slug list hourly
})
const products = await res.json()
return products.map((p: Product) => ({ slug: p.slug }))
}
export default async function ProductPage({
params
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
const res = await fetch(`${process.env.CMS_API}/products/${slug}`, {
headers: { 'Authorization': `Bearer ${process.env.CMS_TOKEN}` },
next: { revalidate: 60 } // ISR: regenerate at most once per 60s
})
if (!res.ok) notFound()
const product: Product = await res.json()
return (
{product.name}
{product.description}
${product.price}
Last updated: {new Date(product.updatedAt).toLocaleDateString()}
)
}
2. Deploy to Vercel
# Run locally first to verify
npm run build
# Deploy (requires Vercel CLI auth)
vercel --prod
Where to run: Local machine or CI/CD pipeline. Requires Vercel account and project linked.
Permissions: Vercel deploy permissions; CMS API token with read access.
Placeholders: CMS_API, CMS_TOKEN — set in Vercel project Environment Variables.
3. Validate ISR Behavior
- Visit the deployed product page. Note the timestamp.
- In your CMS, update the product description.
- Wait 60–90 seconds (the
revalidatewindow plus propagation). - Refresh the page in an incognito window (bypasses browser cache).
- Confirm the new description appears.
Expected check: Updated content appears within ~90 seconds. If not, check Vercel Function logs for revalidate errors or CMS API failures.
Risk: If CMS API is down during background regeneration, the stale page continues serving. No automatic retry—next visitor triggers another attempt.
Concrete Implementation: On-Demand Revalidation with Next.js 14 App Router
Use this when stakeholders need "publish and see it now" for specific pages. Requires a secured API route and CMS webhook configuration.
1. Create the Revalidation API Route
// app/api/revalidate/route.ts
import { revalidatePath } from 'next/cache'
import { NextRequest, NextResponse } from 'next/server'
export async function POST(request: NextRequest) {
// Verify webhook secret
const secret = request.nextUrl.searchParams.get('secret')
if (secret !== process.env.REVALIDATION_SECRET) {
return NextResponse.json({ message: 'Invalid secret' }, { status: 401 })
}
const body = await request.json()
// Expect { slug: 'product-slug' } or { tags: ['products', 'featured'] }
const { slug, tags } = body
try {
if (slug) {
await revalidatePath(`/products/${slug}`)
}
if (tags?.length) {
for (const tag of tags) {
await revalidateTag(tag)
}
}
return NextResponse.json({ revalidated: true, slug, tags })
} catch (err) {
console.error('Revalidation failed:', err)
return NextResponse.json({ message: 'Error revalidating' }, { status: 500 })
}
}
2. Configure CMS Webhook (Contentful Example)
- Go to Contentful Settings → Webhooks → Add webhook
- URL:
https://your-domain.vercel.app/api/revalidate?secret=YOUR_SECRET - Trigger: "Publish" and "Unpublish" for Content Type "Product"
- Headers:
Content-Type: application/json - Payload: Custom — include
{ "slug": "{{entry.fields.slug}}", "tags": ["products"] }
Where to run: CMS admin UI. Requires Contentful admin role.
Permissions: CMS webhook management; Vercel deployment with REVALIDATION_SECRET env var set.
3. Update Page to Use Tags
// In the product page fetch:
const res = await fetch(`${process.env.CMS_API}/products/${slug}`, {
headers: { 'Authorization': `Bearer ${process.env.CMS_TOKEN}` },
next: { tags: ['products', `product-${slug}`] } // Tag for granular invalidation
})
4. Validate End-to-End
- Deploy the API route and updated page.
- In Contentful, publish a change to a product.
- Check Vercel Function logs: you should see a POST to
/api/revalidatewith 200 response. - Visit the product page in incognito—updated content should appear within 5–10 seconds.
Limitation: revalidatePath purges the page cache but not necessarily all edge nodes instantly. Vercel's global purge typically completes in <5s; other providers vary.
Rollback: If webhook storms cause issues, disable the webhook in CMS immediately. No code rollback needed—the API route is idempotent.
Framework-Specific Reality Check
| Framework | ISR Support | On-Demand Support | Notes |
|---|---|---|---|
| Next.js 14+ (App Router) | Native via next: { revalidate } | Native via revalidatePath/revalidateTag | Best overall support; APIs stable since 13.4 |
| Astro 4+ | Hybrid rendering via getStaticPaths + adapter | Via adapter (Netlify, Vercel, Cloudflare) | Requires adapter config; not zero-config like Next.js |
| Remix / React Router v7 | Not supported (favors SSR) | Limited (manual cache control) | Philosophy: server-render everything; use CDN caching headers instead |
| Hugo / Jekyll / Eleventy | None | None | Full rebuild only; use build hooks for automation |
Hosting Platform Gotchas
- Vercel: Native ISR, zero config.
revalidateworks out of the box. On-demand via API routes. Fair use policy: excessive ISR regenerations may be throttled. - Netlify: ISR requires Edge Functions (beta as of 2024). Enable in
netlify.toml:[functions] node_bundler = "esbuild"and use@netlify/edge-functions. On-demand works via Next.js API routes on Netlify Functions. - Cloudflare Pages: ISR via Functions + Workers KV or D1 for caching. Write a Function that checks KV, fetches origin on miss, stores with TTL. More DIY.
- AWS Amplify: ISR via Lambda@Edge + CloudFront. Configure in
amplify.ymlwithcustomHeadersandrewrites. Complex setup; consider if already on AWS.
Decision Checklist
Answer these to pick your strategy:
- Pages > 1,000? → Eliminate full rebuild.
- Need "publish and see now"? → On-demand (if you can secure webhooks).
- Can tolerate 60s staleness? → ISR (simplest ops).
- Hosting on Vercel? → ISR or on-demand both work natively.
- Hosting on Netlify free tier? → ISR via Edge Functions (beta) or full rebuild if pages < 2,000.
- Team has no backend capacity? → ISR or full rebuild only.
- Using Hugo/Jekyll? → Full rebuild only; accept build times.
How to Verify Your Choice Works
Before committing, run a 2-week pilot:
- Pick 10 representative pages.
- Implement your chosen strategy for just those pages.
- Simulate 20 content updates across different times of day.
- Measure: time-to-live-update, build minutes consumed, error rate, stakeholder feedback.
- If ISR: confirm stale window matches
revalidatesetting. - If on-demand: confirm webhook success rate > 99.5% and no duplicate regenerations.
This pilot data beats any benchmark. Adjust revalidate seconds or webhook retry logic based on real traffic patterns.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.