CSR vs SSR vs SSG vs ISR, explained
Four acronyms, one question: where and when does your HTML get built? A plain walkthrough of client-side, server-side, static and incremental rendering, and how to pick between them.
Modern frontend frameworks like Next.js, Nuxt and Astro keep throwing four acronyms at you: CSR, SSR, SSG and ISR. They sound like four different technologies. They are not. They are four answers to the same question.
Where, and when, does the HTML for this page get built?
Once you see it that way, the rest falls into place.
The one idea behind all four
A browser needs HTML to show something. Somebody has to produce that HTML. That somebody can be:
- the browser, after the page loads (CSR),
- the server, every time someone asks (SSR),
- a build script, once, before anyone asks (SSG),
- or a build script that keeps quietly redoing the work as data changes (ISR).
The rest of this post walks through each one.
CSR: Client-Side Rendering
This is how a classic React single-page app works.
- The server sends an almost empty HTML file: a
<div id="root"></div>and a script tag. - The browser downloads the JavaScript bundle.
- The JavaScript runs, calls your API for data, and builds the page in the browser.
<body>
<div id="root"></div>
<script src="/bundle.js"></script>
</body>What you get
- After the first load, navigation is fast. No full page reloads, and the server only sends JSON.
- Hosting is cheap. It is just static files.
- Great for dashboards, admin panels and anything behind a login.
What it costs
- The user stares at a blank screen until the JS downloads, runs and fetches data.
- Search engines and link previews see an empty shell, unless they run your JavaScript. Some do, many do not, and none of them do it well.
- Slow phones pay the price, since they do all the rendering work.
SSR: Server-Side Rendering
With SSR, the server builds the HTML for every request.
- The browser asks for
/products/42. - The server fetches the data, renders the full HTML, and sends it back.
- The browser shows it immediately, then downloads JavaScript to make it interactive. We call that second step hydration.
What you get
- The user sees real content on the first paint.
- Crawlers and link previews get complete HTML. Good for SEO.
- The page can be different per user or per request: cookies, location, A/B tests, live prices.
What it costs
- Every request does work. More traffic means more server cost.
- The page cannot start until the data is ready. A slow database means a slow first byte.
- You need a server running somewhere. No plain static hosting.
SSG: Static Site Generation
SSG moves the work to build time. When you deploy, a script renders every page to an HTML file. After that, the server just hands out files.
This blog works this way. Each post is an MDX file. At build time, Next.js turns it into HTML, and generateStaticParams tells it which slugs to build.
What you get
- The fastest option. A CDN serves a file from a location near the user. No rendering, no database.
- Cheap and reliable. Almost nothing can break at request time.
- Great SEO, since the HTML is complete.
What it costs
- The page is frozen at the moment of the build. If the data changes, the page is stale until you rebuild.
- Build time grows with page count. Ten thousand product pages means a long build.
- It cannot show anything personal, since everyone gets the same file.
ISR: Incremental Static Regeneration
ISR exists because SSG has a real problem. You want static speed, but your content changes. Rebuilding the whole site for one edited product is wasteful.
ISR keeps the static file but lets it refresh itself. You give each page a time window, say 60 seconds. Then:
- Within the window, visitors get the cached static page.
- After the window, the next visitor still gets the cached page, instantly. The server then re-renders the page in the background.
- When that finishes, the server replaces the cached file. The next visitor sees the new version.
This pattern has a name: stale-while-revalidate. Nobody ever waits for a render. The trade-off is that one unlucky visitor per window sees data that is slightly old.
In the Next.js App Router, it is one line:
// app/products/[id]/page.tsx
export const revalidate = 60; // secondsYou can also refresh a page on demand, such as when an editor hits publish in your CMS. Use revalidatePath or revalidateTag for this. The server can also build a page on its first visit, then cache it, even if that page did not exist at deploy time. That is the "incremental" part.
What you get
- Static speed with content that stays reasonably fresh.
- No giant rebuilds. Only the pages that need it get regenerated.
What it costs
- Data can be stale for up to one window. Not fine for stock prices or seat availability.
- It needs a host that supports it. A pure static file host will not do.
- Cache behaviour is harder to reason about than plain SSG or SSR.
Side by side
| CSR | SSR | SSG | ISR | |
|---|---|---|---|---|
| HTML built | In the browser | Per request | At build | At build, then refreshed |
| First paint | Slow | Good | Best | Best |
| Data freshness | Live | Live | Frozen until rebuild | Fresh within a window |
| SEO | Weak | Strong | Strong | Strong |
| Server cost | Lowest | Highest | Lowest | Low |
| Per-user pages | Yes | Yes | No | No |
How to pick
Start at the top of the diagram and answer each question.
Some examples:
- A blog or docs site: SSG. The content changes when you publish, so a rebuild is fine.
- An e-commerce product page: ISR. Prices and stock change, but not every second. Thousands of pages are too many to rebuild at once.
- A logged-in dashboard: CSR. No SEO needed, and the data is personal.
- A search results page or a personalised feed: SSR. It depends on the request, and it should still be crawlable and fast on first load.
You do not have to choose one
Modern frameworks let you pick per page, and even per component. A single Next.js app can have a static marketing page, an ISR product catalogue, an SSR checkout and a CSR dashboard. The static page can also contain a small client-rendered widget, like a cart count.
That is the real takeaway. These are not four competing camps. They are four tools, and each page of your app should use the cheapest one that still gives the user the right content.
That's it for today, until next time~