Frontend · Next.js
SSR vs. SSG vs. ISR: Choosing a Rendering Strategy
Three ways to turn a page into HTML — and the two questions that actually decide which one you need.
Every page a browser renders started life as HTML generated somewhere, at some point in time. The interesting question isn't whether that happens — it's when. Next.js gives you three answers: generate it on every request, generate it once at build time, or generate it once and then quietly refresh it in the background. Picking the wrong one doesn't usually break a site, but it does mean paying for freshness you don't need, or serving staleness your users can't tolerate.
SSR: fresh on every request
With Server-Side Rendering, nothing is pre-built. When a request comes in, the server runs your page's data fetching and rendering logic right then, produces a fresh HTML document, and sends it down the wire. The next request — even one millisecond later, for the exact same URL — repeats the whole process from scratch.
That guarantees the response always reflects the current state of your data: a stock ticker, a logged-in user's dashboard, an inventory count that changes by the second. The cost is that your server's render time and data-fetch time become part of every page load's response time. If your database query takes 400ms, every visitor waits at least 400ms, no matter how many people asked for the same page a second ago. SSR trades freshness for latency and server load, and it scales that cost linearly with traffic.
SSG: built once, served forever
Static Site Generation flips the timing entirely. The HTML is generated once, at build time, before any user ever requests it. That output is then just a file — typically pushed to a CDN and served from edge locations close to the visitor. There's no server-side render step on the request path at all, so response times are about as good as they can get: it's a static asset.
The catch is right there in "built once." If the underlying content changes — a typo fix, a price update, a new blog post — the page keeps serving the old HTML until someone triggers a full rebuild and redeploy. For a marketing page or a documentation site that changes on a schedule you control, that's a non-issue. For anything that changes on its own, it means your site is only ever as fresh as your last deploy.
ISR: the middle ground that doesn't require a rebuild
Incremental Static Regeneration keeps SSG's serving model — visitors get a pre-built static page straight from the cache — but removes the "frozen until redeploy" constraint. You attach a revalidation interval to a page, and after that window elapses, the next request triggers a background regeneration: the stale page is still served immediately (so nobody waits on it), and once the new version finishes rendering, it quietly replaces the old one in the cache for everyone after.
// app/products/[id]/page.tsx
export const revalidate = 60; // seconds
export default async function ProductPage({ params }: { params: { id: string } }) {
const product = await getProduct(params.id);
return <ProductView product={product} />;
}No rebuild, no redeploy, no CI pipeline involved — just a timestamp that tells the framework "this page is allowed to go stale for up to 60 seconds before it's worth regenerating." Most frameworks also support triggering that regeneration on demand — from a CMS webhook when an editor publishes, for instance — rather than waiting for the interval to lapse at all.
Stale-while-revalidate, applied to whole pages
If you've used HTTP's stale-while-revalidate caching directive, ISR is the same idea applied to an entire page's HTML instead of a single response header. Serve what you have, refresh it behind the scenes, and let the next visitor benefit.
Choosing between them
The decision comes down to two questions: how often does the underlying data change, and how fresh does it need to be for the user looking at it right now?
- Rarely changes, staleness is fine — a marketing page, a pricing page, a docs site. Reach for SSG. There's no reason to pay a render cost on every request for content that hasn't moved in weeks.
- Changes constantly, or is different per user — a personalized dashboard, a live auction, an authenticated account page. Reach for SSR. Any cached or pre-built version would be wrong the moment it's viewed.
- Changes periodically, some staleness is tolerable — a blog, a product catalog, a news listing. Reach for ISR. Users get static-speed responses almost all the time, and the content catches up within a bounded, predictable window without anyone running a deploy.
Notice that none of these are permanent labels for a route — the same Next.js app can render its landing page with SSG, its blog posts with ISR, and its account settings page with SSR, all side by side. The strategy is a property of the page's data, not the framework or the project as a whole.
A different axis from Server vs. Client Components
It's easy to conflate rendering strategy with the Server Components vs. Client Components split, but they answer different questions. SSR/SSG/ISR is about when the HTML gets generated — at request time, at build time, or on a revalidation schedule. Server vs. Client Components is about how much JavaScript ships to the browser — whether a piece of UI renders only on the server and sends down markup, or hydrates and re-renders on the client. A page can be statically generated (SSG) and made up mostly of Server Components, or server-rendered on every request (SSR) while still shipping a Client Component for its one interactive widget. The two choices compose independently; treating them as the same decision is a common source of confusion.
Key takeaways
- SSR renders on every request — always fresh, but the server's render time is added to every response.
- SSG renders once at build time and serves static output — extremely fast, but frozen until the next rebuild and redeploy.
- ISR serves static pages like SSG but can regenerate them in the background on a revalidation interval or on-demand trigger, without a full rebuild.
- Choose based on two questions: how often the data changes, and how fresh it needs to be for the viewer.
- SSG for rarely-changing content, SSR for per-user or real-time data, ISR for periodically-changing content that tolerates some staleness.
- Rendering strategy (when HTML is generated) and Server/Client Components (how much JS ships) are independent axes that compose together.
