When Full Static Builds Stop Scaling

Static Site Generation (SSG) solves a lot of problems: pre-rendered pages are fast, resilient, and immediately available from a CDN. But as sites grow, the model breaks down. Build time scales linearly with page count. At a hypothetical 1ms per page, generating millions of product pages still takes hours. For large web applications, complete static generation simply isn't viable — the tradeoff between freshness and build duration becomes unacceptable.

Build time graph
The Problem with Static-Site Generation: Because build-times scale linearly with the number of pages, you might be stuck waiting for hours for your site to build. (Large preview)

The content problem compounds this. Most sites use a Headless CMS so editors can publish without touching code. But with traditional SSG, a single price change on one of 100,000 products triggers a webhook that rebuilds everything. Waiting hours for a promotion to go live is not an option, and paying for that unnecessary computation is wasteful. The ideal system understands which pages changed and updates only those, without a full rebuild.

ISR: Static Pages, Updated On Demand

Incremental Static Regeneration (ISR) is Next.js's answer to that problem. It lets you create or update static pages after the initial build, on a per-page basis. ISR retains the performance and resilience of static content while allowing pages to be generated at runtime on demand — no full site rebuild required.

Returning to the 100,000-product store: at a realistic 50ms per page, a full static build takes almost two hours. ISR changes the calculus. You can choose your tradeoff:

  • Faster builds: Generate the most popular 1,000 products at build time. Requests for other products become cache misses, generating statically on demand. Build time: roughly one minute.
  • Higher cache hit rate: Generate 10,000 products at build time, ensuring more pages are served from cache immediately. Build time: roughly eight minutes.
An illutration showing Jamstack on the left and Incremental Static Regenertion on the right
The advantage of ISR: You have the flexibility to choose which pages are generated at build or on-demand. Choose from (A) faster builds or (B) more cached. (Large preview)

How To Use ISR In Next.js

ISR builds on the standard SSG API. You fetch data in getStaticProps and add a revalidate property to enable regeneration for that page:

A diagram of the request flow for Incremental Static Regeneration
A diagram of the request flow for Incremental Static Regeneration. (Large preview)

The lifecycle works as follows:

  1. Set a revalidation time per page — for example, revalidate: 60 seconds.
  2. The first request shows the cached page with its original data.
  3. Content editors update the data in the CMS.
  4. Requests within the 60-second window are served instantly from cache.
  5. After the window passes, the next request shows the stale cached page while Next.js triggers a background regeneration.
  6. When regeneration succeeds, the cache is invalidated and subsequent requests show fresh content. If regeneration fails, the old page remains intact.
// pages/products/[id].js

export async function getStaticProps({ params }) {
  return {
    props: {
      product: await getProductFromDatabase(params.id)
    },
    revalidate: 60
  }
}

Defining Which Paths To Generate

Use getStaticPaths to specify the pages generated at build time — say, the top 1,000 product IDs. For all other product requests, Next.js needs a fallback strategy:

  • fallback: blocking (preferred): The first request to an ungenerated page is server-rendered, then cached as static for all future requests.
  • fallback: true: The first request immediately returns a static page with a loading state. When data loading completes, the page re-renders with new data and is cached.
// pages/products/[id].js

export async function getStaticPaths() {
  const products = await getTop1000Products()
  const paths = products.map((product) => ({
    params: { id: product.id }
  }))

  return { paths, fallback: 'blocking' }
}

Tradeoffs And Alternatives

ISR is a tool, not a universal default. Next.js supports multiple rendering strategies so you can choose per page.

When To Prefer Server-Side Rendering

SSR makes sense when stale content is unacceptable — a news feed, for example. With SSR you can also customize the page based on the incoming request, and set your own cache-control headers with surrogate keys for invalidation. The main difference from ISR lies in the first request: ISR can guarantee a static response if the page was pre-rendered. SSR relies on server availability for every first visit. But note: SSR without caching hurts performance — server-side blocking work directly impacts Time to First Byte (TTFB). Choose SSR carefully, and pair it with proper caching headers.

// You can cache SSR pages at the edge using Next.js
// inside both getServerSideProps and API Routes
res.setHeader('Cache-Control', 's-maxage=60, stale-while-revalidate');

When To Stick With Plain SSG

For small sites, ISR adds unnecessary complexity. If the revalidation period exceeds the total build time, traditional SSG is the simpler and more appropriate choice.

Client-Side Rendering Limitations

CSR (React without Next.js) has no server component, which broadens hosting options. But initial HTML lacks pre-rendered content, hurting SEO and making the app unusable when JavaScript is disabled. The loading-state-first model also delays first meaningful paint.

Choosing A Fallback Strategy

With fast data fetching, fallback: blocking is cleaner: no loading state, and the response looks the same whether cached or not. With slow data fetching, fallback: true lets you show loading UI immediately.

Beyond Caching: Persistence And Rollbacks

ISR is not merely a caching layer. Generated pages are persisted between deployments, enabling instant rollbacks without losing previously generated content. Deployments are keyed by ID; Next.js uses this ID to persist static pages. Rolling back updates the key to the previous deployment, so old pages serve correctly. However, persisting pages and rollback behavior are managed by your hosting provider, not by Next.js itself. Unlike Cache-Control-based server rendering, where caches expire and reset across regions, ISR pages survive redeployment.

A practical example:

  • Deployment ID 123 contains a typo: "Smshng Magazine".
  • Editors fix the typo in the CMS. No redeploy needed. The regenerated page shows "Smashing Magazine" and is persisted.
  • Bad code goes out as deployment ID 345.
  • You roll back to deployment ID 123. The corrected page — with "Smashing Magazine" — is still served.

Putting ISR To Work

ISR suits content that changes periodically rather than continuously. Typical use cases include e-commerce product catalogs, marketing pages, blog posts, and ad-backed media.

Next.js supports ISR out of the box via next start. Given the flexibility to blend SSG, ISR, and SSR within a single project, you can adopt the approach progressively —
adding static generation, then on-demand regeneration, one page at a time, without rewriting existing code.