Static Generation and Revalidation
Date: 2026-08-16
Render pages at build time, then refresh them in the background without rebuilding everything. It gives you a static file’s speed with a database-backed site’s freshness, at the cost of a staleness window you have to choose deliberately.
What it is
Static generation (SSG) renders pages to HTML at build time. Revalidation refreshes those pages after deployment, so the catalogue can change without a rebuild.
The problem it solves: pure SSG is fastest and can’t cope with a large or changing catalogue. A 200,000-SKU shop can’t rebuild every time a price moves — builds measured in hours, triggered several times a day.
Incremental static regeneration
ISR is the common implementation:
request for /products/socks
│
├─ page exists and is fresh → serve it. instant
│
├─ page exists and is stale → SERVE THE STALE ONE immediately,
│ regenerate in the background,
│ next request gets the fresh one
│
└─ page doesn't exist yet → render on demand, cache it,
serve it (first visitor waits)
Two properties do the work:
- Nobody waits for regeneration. Staleness is traded for speed, and the trade is invisible to the user
- Pages are generated on demand. You don’t build 200,000 pages; you build the popular ones and let the long tail generate when someone asks
This is stale-while-revalidate applied at the page level — same idea as HTTP Caching, one layer up.
Choosing the window
The revalidation interval is a business decision, not a technical one:
homepage 60s merchandising changes often
category listings 300s new products should appear reasonably fast
product pages 3600s description and imagery rarely change
blog / editorial 86400s effectively static
legal pages 604800s annual
Price and stock are the exception, and the reason ISR alone isn’t enough for retail. Serving an hour-old price is a commercial and, for pricing claims, a compliance problem.
The standard resolution: statically render the page, fetch price and availability client-side or at the edge. The page is fast and indexable; the two volatile fields are live. Reserve space for them so the update doesn’t cause Cumulative Layout Shift.
On-demand revalidation
Better than a timer where you have a clear change event:
merchandiser publishes a price change
→ webhook fires
→ revalidate /products/socks and its category pages
→ next request gets fresh content
Timer-based revalidation is a guess about how often things change. Event-based is exact, and it means you can set long intervals as a safety net rather than as the mechanism. It needs the platform to emit reliable events, and it needs you to know which pages a change affects — the second is the harder part, since a price change touches the product, its categories, search results and any homepage module featuring it.
The tradeoffs
- Staleness is real. Someone will see the old version. Decide which fields that’s acceptable for
- First request to an ungenerated page is slow — a full server render. Pre-generate the pages you know matter
- Cache invalidation across a CDN is a second layer with its own propagation delay — CDN Caching
- Personalisation is incompatible with the cached page. Cache the shell, personalise separately
- Debugging is harder. “Which version am I looking at, and when was it generated?” needs to be answerable, ideally from a response header
- Implementation is framework-specific. The concept is portable; the API isn’t
Where it fits
For a retail site, the usual shape:
SSG content pages, landing pages
ISR category and product pages, revalidated on publish
client price, stock, personalised modules
SSR search results, basket, checkout
That combination gives fast indexable pages for everything a crawler cares about, live data where it’s commercially necessary, and dynamic rendering only where it’s genuinely required — see Rendering Strategies and Rendering and SEO.