Vercel ISR vs SSG for Content Sites: Which Wins

ISR or plain SSG for your content site on Vercel? The real trade-off in build time, freshness, function cost, and rollback, with current 2026 Vercel limits and config you can copy.

SSG pre-renders every page at build and serves it from the CDN until the next build. ISR pre-renders too, but Vercel re-renders pages in the background, either on a timer or on demand. For a content site the decision is not philosophical. It comes down to four measurable things: how long your build takes, how fresh pages need to be, what serverless functions will cost you, and how messy rollback gets.

TL;DR

  • Under ~500 articles, edits a few times a week, build under 3 minutes: SSG. It is cheaper, simpler, and rolls back atomically with one click.
  • Over ~2,000 articles, or many edits per day, or builds creeping past 10 minutes: ISR. Pre-render the hot slugs at build, regenerate the rest on demand.
  • In between: pick by your editorial workflow, not benchmarks. If a redeploy is painless, stay on SSG.
  • Vercel’s build cap is 45 minutes on every plan (Hobby, Pro, and Enterprise) as of June 2026, so “we hit the build limit” is the single loudest reason to move to ISR.

What SSG and ISR actually do on Vercel

SSG (Static Site Generation) writes HTML during next build or astro build. Every URL becomes a file in out/ or dist/, uploaded to the CDN. No function runs at request time, so the cost is essentially CDN bandwidth.

ISR (Incremental Static Regeneration) still pre-renders pages, but a page can go stale and trigger a background re-render. With time-based revalidation, the cached page is served immediately and a fresh render kicks off when the page is older than your revalidate window (stale-while-revalidate). With on-demand revalidation, you explicitly invalidate a path or tag (for example from a CMS webhook) and the next request renders fresh. Either way, ISR needs a Node serverless function deployed as the renderer, which changes the cost model.

ISR began as a Next.js feature, but as of June 2026 the Astro Vercel adapter ships ISR too. More on that below, because the old “Astro has no real ISR” advice is now out of date.

Vercel limits that decide this for you (June 2026)

These are the platform numbers that actually force the choice. Verified against Vercel’s current limits documentation.

LimitHobbyPro
Build time per deployment45 min45 min
Build execution included6,000 min/moPay-as-you-go credit
Concurrent builds112
Deployments per day1006,000
ISR Reads includedn/a (Hobby usage caps apply)First 10,000,000
ISR Writes includedn/aFirst 2,000,000
Function max duration60 s300 s

Two things people get wrong here. First, the 45-minute build cap is the same on every plan; upgrading to Pro does not buy you a longer build, it buys concurrency (12 parallel builds vs 1) and pay-as-you-go headroom. Second, ISR is metered separately as ISR Reads and ISR Writes, so a page set to regenerate aggressively is a recurring line item, not a free static asset.

When SSG is the right answer

Stay on SSG if most of these are true:

  • Your build finishes in under 3 minutes, so redeploying for a typo is no pain.
  • Editors are comfortable with git, or your CMS already triggers a redeploy on save.
  • You want the most boring production possible: the CDN serves files, zero function invocations, nothing to debug at 2 a.m.
  • Rollback is “promote the previous deployment,” which is instant and atomic across all pages with no cache to evict.
  • You want the bill to be roughly the cost of a CDN, full stop.

Astro defaults to SSG with output: 'static'. Next.js renders a route statically when it has generateStaticParams and no request-time dynamic APIs.

This site itself is a few thousand pages on plain SSG, and the build is comfortably inside the cap. Most content sites never outgrow it.

When ISR pays for itself

Three concrete cases where the function cost earns its keep.

1. Build time is the bottleneck. At 5,000+ articles a cold build can run 15-20 minutes, so every typo fix is a long wait, and you are one slow build away from the 45-minute cap. ISR pre-renders only the popular slugs and lazily generates the rest:

// app/articles/[slug]/page.tsx
export const revalidate = 3600; // regenerate at most once an hour

export async function generateStaticParams() {
  // Pre-render the top 200 at build; the long tail renders on first request
  return (await getTopArticles(200)).map((a) => ({ slug: a.slug }));
}

2. On-demand revalidation from a CMS webhook. An editor clicks Publish in Sanity or Contentful, the webhook calls your route, and only the affected URL regenerates in seconds. No full redeploy, no 20-deploys-a-day churn:

// app/api/revalidate/route.ts
import { revalidatePath, revalidateTag } from 'next/cache';
import { NextRequest, NextResponse } from 'next/server';

export async function POST(req: NextRequest) {
  const secret = req.nextUrl.searchParams.get('secret');
  if (secret !== process.env.REVALIDATE_SECRET) {
    return NextResponse.json({ ok: false }, { status: 401 });
  }
  const { slug, tag } = await req.json();
  if (tag) revalidateTag(tag); // invalidate many pages sharing a tag
  if (slug) revalidatePath(`/articles/${slug}/`);
  return NextResponse.json({ ok: true, revalidated: slug ?? tag });
}

Reach for revalidateTag when one edit should refresh a group of pages (say, every article in a category) without you tracking each URL. Reach for revalidatePath for a single known route.

3. Scheduled refresh of a fragment. The article body is fixed, but a “related articles” rail or a price table needs refreshing weekly. Set revalidate = 604800 (7 days) and the page rebuilds itself in the background.

Astro ISR is real now (config you can copy)

The widely repeated claim that “Astro has no true ISR” is out of date as of June 2026. The @astrojs/vercel adapter exposes an isr option that caches on-demand-rendered pages exactly like prerendered ones after the first request.

// astro.config.mjs
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel';

export default defineConfig({
  output: 'server',
  adapter: vercel({
    isr: {
      // time-based: cache each page for one day
      expiration: 60 * 60 * 24,
      // on-demand: lets you invalidate via the x-prerender-revalidate header
      bypassToken: process.env.ISR_BYPASS_TOKEN,
      // never cache these
      exclude: ['/preview', /^\/api\/.+/],
    },
  }),
});

To invalidate on demand, send a GET or HEAD request to the page URL with the x-prerender-revalidate header set to your bypassToken. That is the Astro equivalent of revalidatePath. So the honest 2026 answer is: Astro content sites are usually fine on SSG, but if you want ISR, the adapter gives it to you, including on-demand invalidation.

Rollback is where they really differ

  • SSG rollback is one click. Vercel promotes the previous build and the swap is atomic across every page. The previous deployment is already fully built.
  • ISR rollback is trickier. Promoting an older deployment does not retroactively re-render pages that were regenerated under the newer one, so the runtime cache can still hold stale HTML. After you promote, fire revalidatePath / revalidateTag (or hit the Astro bypass token endpoint) to flush the affected pages, or wait out the TTL.

If an instant, guaranteed-clean rollback matters more than build speed, that alone can keep you on SSG.

Common mistakes

  • Setting revalidate = 60 on every page “to be safe.” You just turned the first request after every minute into a function invocation and a billable ISR Write. The bill climbs and the CDN hit rate drops, for content that changes monthly.
  • Using ISR with no on-demand hook. Editors still wait out the revalidate window to see their change live. Add the webhook.
  • Forgetting ISR needs Node serverless functions, not just static files. This changes both your cost model and the metered ISR Reads/Writes you consume.
  • Mixing SSG and ISR and being confused why one page is stale. Check export const revalidate (Next.js) or the adapter isr config (Astro) for that segment.
  • Treating Astro output: 'hybrid' as ISR. Hybrid just means static-by-default with per-page prerender = false for SSR routes. ISR is the separate adapter isr option shown above.

FAQ

Is ISR worse for SEO than SSG? No. Both serve fully pre-rendered HTML to crawlers. The only difference is that the very first regenerate after a deploy can be a touch slower for one visitor; every hit after that is CDN-fast, and Googlebot almost never lands on that exact cold render.

Does ISR cost more than SSG? It can. SSG is essentially CDN bandwidth. ISR adds metered ISR Reads (first 10M free on Pro as of June 2026) and ISR Writes (first 2M free), plus function invocations. For a low-traffic site that rarely changes, that is noise; for a high-traffic site with aggressive revalidation, it adds up. Set conservative revalidate windows and prefer on-demand invalidation.

Can I really use ISR with Astro now? Yes. Set output: 'server' and pass an isr option to @astrojs/vercel (expiration, bypassToken, exclude). On-demand invalidation works via the x-prerender-revalidate header. The older “Astro has no ISR” advice predates this.

What about Vercel’s Data Cache? It is orthogonal. The Data Cache caches individual fetch() calls; ISR caches the rendered page. You can combine them: ISR caches the page output, the Data Cache caches the API calls made inside that render so a regenerate is cheaper.

What happens if a regenerate fails? The last-known-good HTML keeps serving. You will see the error in Vercel runtime logs (kept 1 hour on Hobby, 1 day on Pro), but users never see a broken page. Graceful degradation is the entire point of stale-while-revalidate.

External references: Vercel limits and the Astro Vercel adapter docs.

Tags: #Indie dev #Vercel #Hosting #isr #Performance