Rendering strategy comparison
SSR vs CSR — Server-Side vs Client-Side Rendering
Two fundamental strategies for delivering web UIs. SSR generates HTML on the server before sending it to the browser — fast first paint, SEO-friendly, great for personalised content. CSR sends a JavaScript bundle and builds the UI entirely in the browser — richer interactivity, faster subsequent navigation, but a slower initial load. Modern frameworks such as Next.js, Nuxt, and SvelteKit blur the boundary by supporting both strategies per page.
TL;DR
- SSR — HTML generated on the server per request. Fast FCP, great SEO, higher server cost. Requires a Node/server runtime. Used in: Next.js, Nuxt, SvelteKit, Remix.
- CSR — Near-empty HTML shell plus a JS bundle. The browser builds the DOM. Rich interactivity, fast subsequent navigation, blank initial HTML for crawlers. Used in: Create React App, plain Vue or React SPAs.
- SSG — HTML pre-built at deploy time and served as static files. Zero server cost per request, CDN-distributed, best raw performance. Used in: Astro, Next.js static export, Gatsby.
- Hydration bridges SSR and CSR: the server sends complete HTML, then the client JS "attaches" to it to add interactivity.
Side-by-side comparison
| Aspect | SSR | CSR |
|---|---|---|
| Initial HTML | Complete HTML from the server | Near-empty shell — content added by JS at runtime |
| SEO | Excellent — crawlers see full content immediately | Risky — Googlebot may index eventually; social crawlers often do not run JS |
| First Contentful Paint (FCP) | Faster — complete HTML arrives ready to display | Slower — browser must download, parse, and execute JS first |
| Time to First Byte (TTFB) | Higher — server must render HTML before responding | Lower — server immediately returns a static shell |
| Subsequent navigation | New server request per route (unless using a hybrid router) | Instant client-side routing — no full page reload |
| Bundle size impact | JS bundle still needed for hydration, but can be smaller | Entire application JS must download before first paint |
| Server cost | Higher — server renders HTML on every request | Lower — server only serves static files; API calls handle data |
| Personalisation | Easy — render user-specific HTML on the server | Requires client-side API calls after the initial load |
| Crawler access | Full content visible on first response | Content hidden until JS executes (problematic for non-Google crawlers) |
| Typical frameworks | Next.js, Nuxt, SvelteKit, Remix, Angular Universal | Create React App, plain Vue SPA, plain React SPA |
What is SSR?
In server-side rendering, the web server receives a request, fetches any required data (from a database, CMS, or API), renders the full HTML document, and sends it to the browser. The browser displays the content immediately — no JavaScript execution required to see the page.
The trade-off is that every request generates server work. A Node.js process (or equivalent) must render the template before the first byte can leave the server, which increases TTFB compared to serving a cached static file. In practice, SSR responses are often cached at the CDN edge with a short TTL to mitigate this.
Once the HTML is in the browser, the page may still need to download a JavaScript bundle so that interactive elements (forms, dropdowns, client-side navigation) work. This "attaching" phase is called hydration. If the JS bundle is large or slow to parse, there can be a visible delay between seeing the page and being able to interact with it — an issue tracked by the Interaction to Next Paint (INP) Core Web Vital.
// Next.js SSR example — runs on the server for every request
export async function getServerSideProps(context) {
const { params } = context;
const product = await db.products.findById(params.id);
return {
props: { product }, // sent as props to the React component
};
} What is CSR?
In client-side rendering, the server sends a minimal HTML document — essentially a <div id="root"></div> and a <script> tag pointing to the application bundle. The browser downloads the bundle, executes it, and React (or Vue, Angular, etc.) builds the entire DOM in the browser.
The initial TTFB is low because the server returns a tiny file instantly. However, FCP is higher because nothing meaningful appears on screen until the JS bundle has been downloaded, parsed, and executed. On slow mobile connections or low-end devices, this creates a frustrating blank-page experience.
Once the application is loaded, subsequent navigation is extremely fast — the JS router swaps components without a round-trip to the server. This is why CSR became the dominant pattern for dashboards and tool-like applications where users spend long sessions after the initial load.
Bundle size is the most important performance lever for CSR. A 1 MB uncompressed bundle on a 3G connection adds several seconds before content appears. Code-splitting by route (loading only the JS for the current page) is the standard mitigation.
// CSR — data fetched in the browser after component mounts
function ProductPage({ id }) {
const [product, setProduct] = React.useState(null);
React.useEffect(() => {
fetch(`/api/products/${id}`)
.then(r => r.json())
.then(setProduct);
}, [id]);
if (!product) return <Spinner />;
return <ProductDetail product={product} />;
} Static Site Generation — the third path
SSG pre-renders every page to a static HTML file at build time. The files are deployed to a CDN; no server computation happens per request. TTFB is minimal, FCP is the fastest achievable, and there is no server to scale or crash.
The limitation is freshness — content is only as up-to-date as the last build. For blogs, documentation, or marketing pages that update a few times a day, this is fine. For a live stock ticker, it is not.
Next.js introduced Incremental Static Regeneration (ISR) to address this: pages are statically generated but automatically regenerated in the background on a configurable interval, combining SSG performance with SSR freshness.
Choosing between SSG, SSR, and CSR is similar in some ways to choosing between microservices and a monolith — the "right" answer depends on your traffic patterns, team size, and content update frequency, not on which technology sounds more modern.
Perceived performance and the role of hydration
A page can look fast but feel slow. SSR delivers visible content quickly (good FCP), but if the hydration bundle is large, the page may appear interactive whilst actually ignoring clicks — a phenomenon sometimes called the hydration gap.
Modern frameworks address this with selective hydration strategies:
- Islands architecture (Astro, Fresh) — only explicitly interactive components ship JavaScript; the rest is static HTML.
- React Server Components — server-only components produce UI with zero client JS; only "use client" components hydrate.
- Streaming SSR — HTML is streamed to the browser progressively; the browser can render and hydrate parts of the page before the full response arrives (Next.js App Router, React 18's
Suspense).
Understanding these trade-offs is essential vocabulary for frontend architecture discussions, similar to how REST vs GraphQL debates centre on data-fetching efficiency.
How engineers talk about SSR vs CSR
SSR conversations
- "The page is server-rendered — SEO crawlers see the full HTML on first byte."
- "We use getServerSideProps to fetch data before the page is sent down the wire."
- "Hydration takes 200 ms — the page looks interactive before the JS has actually attached."
- "We cache the SSR output at the CDN edge for 60 seconds to reduce origin load."
- "The TTFB is higher than a static site because the server has to render HTML on every request."
- "We switched to streaming SSR so the browser can start rendering the shell while the data fetch is still in flight."
CSR conversations
- "The SPA sends an empty div initially — all content is injected by React after the bundle loads."
- "Client-side routing means navigation doesn't reload the page — just swaps components."
- "The bundle is 800 kB — we need to code-split by route or Lighthouse will flag it."
- "Lighthouse shows a poor FCP because JS must execute before anything is painted."
- "We pre-render the Open Graph meta tags server-side to fix blank social previews."
- "The dashboard is CSR — it's behind auth anyway so SEO is irrelevant."
Decision guide
- SEO-critical page (blog, product listing, marketing) → SSG or SSR
- Content is the same for all users and changes infrequently → SSG
- Content is personalised per user but SEO still matters → SSR
- Content updates very frequently (live data) with SEO needs → SSR with short CDN TTL or ISR
- Authenticated dashboard, admin tool, no SEO requirement → CSR is fine
- Social sharing previews (og:image, og:title) must work → SSR or SSG
- Maximum performance, scale to zero server cost → SSG + CDN
- Complex app — different pages have different needs → Mix strategies per page (Next.js, SvelteKit)
Key vocabulary
- SSG (Static Site Generation)
- HTML generated at build time and served as static files — no server computation per request. Fastest delivery, lowest cost, but requires a rebuild (or ISR) for content changes.
- Hydration
- Attaching JavaScript framework logic (event handlers, component state) to HTML that was already rendered by the server. The page visually appears before it becomes interactive.
- TTFB (Time to First Byte)
- How long after a browser sends a request before the first byte of the response arrives. Reflects server processing time and network latency. Lower is better for perceived speed.
- FCP (First Contentful Paint)
- A Core Web Vital — the time from navigation start until the first text or image is rendered on screen. SSR typically achieves a lower FCP than CSR for the same content.
- ISR (Incremental Static Regeneration)
- A Next.js feature that statically generates pages at build time but regenerates them in the background at a configurable interval, combining SSG performance with SSR freshness.
- Bundle size
- The total size of the JavaScript files the browser must download before a CSR application can render. Large bundles delay FCP; code-splitting by route is the standard mitigation.
- Crawler access
- Whether a search engine or social media bot can read a page's content. SSR and SSG guarantee full content in the initial HTML; CSR depends on the crawler executing JavaScript.
Frequently asked questions
What is the difference between SSR and CSR?
SSR (Server-Side Rendering) generates the full HTML on the server for each request — the browser receives complete markup and can display it immediately. CSR (Client-Side Rendering) sends a near-empty HTML shell plus a JavaScript bundle; the browser runs the JS to build the DOM. SSR delivers faster First Contentful Paint and better SEO; CSR delivers richer client-side interactivity and faster navigation after the initial load.
What is SSG (Static Site Generation)?
SSG generates HTML at build time rather than on each request. Pages are pre-rendered and served as static files — typically from a CDN — with no server computation per request. This gives the best performance and lowest cost for content that does not change per user (blogs, docs, marketing pages). Frameworks such as Next.js, Astro, and Gatsby all support SSG. Many sites mix strategies: SSG for static content, SSR for personalised pages.
Does CSR hurt SEO?
It used to hurt significantly. Googlebot now executes JavaScript, so CSR pages can be indexed — but rendering is deferred to a second wave, which can delay discovery. SSR and SSG are still recommended for SEO-critical pages because they provide complete HTML on the first response. Social media crawlers (Twitter/X, Facebook, LinkedIn) often do not run JavaScript at all, so CSR pages get blank previews without a server-side meta-tag fallback.
What is hydration and why does it matter?
Hydration is the process of attaching JavaScript event listeners and React/Vue/Angular component logic to server-rendered HTML that already exists in the DOM. The page visually appears complete (from SSR) but is not interactive until hydration finishes. If hydration is slow or the JS bundle is large, there is a gap between when the user sees content and when it responds to clicks — this contributes to poor Interaction to Next Paint (INP) scores and a frustrating user experience.
What is the difference between Time to First Byte (TTFB) and First Contentful Paint (FCP)?
TTFB measures how quickly the server responds with the first byte of the HTTP response — it reflects server and network speed. FCP measures when the browser first renders any visible content on screen. SSR has a higher TTFB than CSR (because the server must render HTML before responding) but a lower FCP (the complete HTML arrives ready to display). CSR has a low TTFB because the server immediately returns a static shell, but a higher FCP because the browser must download, parse, and execute the JS bundle before anything appears.
What are React Server Components?
React Server Components (RSC) are a newer model where components render exclusively on the server and produce UI without shipping their JavaScript to the browser. Server Components can directly access databases and file systems without an API layer. Client Components (marked "use client") handle interactivity. This hybrid model — first shipped in Next.js App Router — aims to give the data-access simplicity of SSR with fine-grained client interactivity and a smaller JS bundle.
When should I use SSG vs SSR vs CSR?
Use SSG when content is the same for all users and changes infrequently — it is the fastest and cheapest option. Use SSR when content is user-specific or changes frequently and SEO matters. Use CSR for authenticated dashboards, admin tools, or any application where SEO is irrelevant and rich interactivity is the priority. Most modern frameworks (Next.js, SvelteKit, Nuxt) let you mix all three strategies per page — this is often the right answer for large applications.