Quick Answer

CSR (client-side rendering) ships a near-empty HTML file and builds the page in the browser with JavaScript. SSG (static site generation) renders every page to HTML once at build time and serves the files. SSR (server-side rendering) renders HTML fresh on the server for each request. The trade-off is freshness and personalization versus cost and speed: SSG is cheapest and fastest but stale between builds, SSR is always current but needs a running server, CSR is simple to host but slow to first paint and weaker for SEO.

One Decision, Three Answers

Every rendering strategy answers the same question: when a user requests a page, does finished HTML for it already exist, and if so, how old is it?

  • CSR: no. The server sends a shell and the browser assembles the page.
  • SSG: yes. It was built when you deployed and is identical for everyone until the next deploy.
  • SSR: yes, but it was built microseconds ago, for this request, and can contain this user's data.

That is the whole taxonomy. Everything else - SEO behaviour, time to first paint, server cost, how you handle auth - follows from where and when the HTML is produced. Modern frameworks (Next.js, Astro, Remix, SvelteKit) let you choose per route, so a real application is usually a mix of all three.

CSR: The Browser Builds It

The server returns something close to:

<div id="root"></div>
<script src="/app.js"></script>

Verified: the #root div is empty in the raw response. It only fills in after app.js downloads, runs, fetches data, and renders - then the DOM shows content. A test that read the served HTML saw an empty root; after the page's own script ran, it read Keyboard - Rs 4999.

Strengths: the server serves static files (cheap, CDN-cacheable), and once loaded, moving between views is instant because it is all client-side.

Costs: the first screen is blank until JavaScript executes - painful on slow devices and networks. There is a request waterfall: HTML, then JS bundle, then API call, then render. And a crawler or link-preview bot that does not run JavaScript sees the empty shell.

Good fit: dashboards, internal tools, anything behind a login where SEO does not matter and users accept a brief load for a rich app.

SSG: Built Once at Deploy

At build time the generator runs your components with data and writes plain HTML files:

// build step
writeFileSync("dist/product.html", renderPage({ name: "Keyboard", stock: 7 }));

Verified: the number 7 is baked into the file and does not change until you rebuild - a request an hour later still says 7 even if stock moved.

Strengths: the response is a static file, so it is as fast and cheap as the web gets, trivially CDN-cached, with no server to keep running or attack. It is complete HTML, so SEO and link previews work with no effort.

Costs: content is only as fresh as your last build, which rules it out for stock, prices, or anything per-user. Build time grows with page count - 100,000 pages can mean long deploys, though incremental and on-demand builds soften that.

Good fit: marketing sites, docs, blogs, changelogs - content that changes on a human schedule, not a per-request one.

SSR: Built Per Request

The server runs your components on every request, with fresh data, and sends complete HTML:

server.on("request", (req, res) => {
  const product = db.getProduct();   // fresh read each time
  res.end(renderPage(product));
});

Verified: two requests to the same URL returned different stock numbers, each reflecting the data at that instant, and the HTML already contained the <h1> - no blank shell to fill in.

Strengths: always current, can be personalized (the signed-in user's name, their cart), and fully crawlable.

Costs: you need a server process running and spending CPU on every request. Time to first byte now depends on how fast your database and APIs respond, so a slow query becomes a slow page. Caching is harder because responses differ per user.

Good fit: pages that must be both fresh and indexable - product detail pages, search results, news, social feeds.

The Gotcha: Hydration Mismatches

SSR and SSG send HTML, then the JavaScript framework hydrates it - re-runs your components in the browser and attaches event listeners, expecting the output to match the HTML it received. When it does not match, you get a warning, and the framework may discard the server markup and re-render on the client, causing a visible flicker or reset.

Verified with React: a component rendering the current time produced Rendered at 10:00:00 on the server and Rendered at 11:54:19 in the browser, and React logged Warning: Text content did not match. Server: ... Client: ... then replaced the text.

The cause is always something that differs between the two environments: Date.now() or new Date(), Math.random(), reads of window or localStorage (undefined on the server), locale-dependent formatting, and typeof window !== "undefined" branches. Fix it by rendering the same thing on both sides and moving the environment-specific part into a useEffect, which only runs in the browser - or by explicitly marking that subtree client-only. Pure CSR never has this bug, because there is no server output to disagree with.

How to Choose

Decide per route, not per app:

  • Same for everyone, changes rarely (landing pages, docs, blog): SSG. Add incremental revalidation if it changes a few times a day.
  • Fresh or personalized but also needs SEO (product pages, listings): SSR, ideally with a short cache for anonymous visitors.
  • Behind a login, SEO irrelevant, app-like (dashboards, editors): CSR, or SSR for the first paint then client-side after.
  • Mostly static with a few dynamic bits: SSG the page and fetch the dynamic parts client-side.

Nothing stops you making every route SSR "to be safe" - that just puts a server in the critical path of pages that never needed one. Start static, and upgrade a route to SSR when it genuinely needs per-request data.

Frequently Asked Questions

Which is best for SEO? SSG and SSR both send complete HTML, so both are fine. Pure CSR sends an empty shell; search engines may still render it, but it is slower and less reliable. Prefer SSG or SSR for public pages.
What is hydration? After SSR or SSG sends HTML, the framework re-runs your components in the browser to attach event handlers and take over. If that re-run produces different output than the HTML, you get a hydration mismatch.
Can one site use all three strategies? Yes, and most do. Frameworks like Next.js and Astro let you set the strategy per route - static marketing pages, server-rendered product pages, client-rendered dashboard.
Is SSR always slower than SSG? Time to first byte is higher because the server renders on demand and it depends on your data fetching. SSG serves a pre-made file so it is faster, but it cannot show per-request data.
What is ISR or revalidation? A middle ground: pages are static but regenerated in the background on a schedule or on demand, so you get SSG speed with fresher content.