A compilation of my understanding on different frontend rendering strategies as part of my interview prep

A A A A A

Rendering strategies, explained

3,548 words ยท 106 sentences ยท 67 paragraphs ยท Avg: 33 words/sentence (Hemingway: ~15) ยท Flesch: 0 Very Confusing

Every web application makes a choice about where and when its HTML gets built. That choice has downstream effects on performance, SEO, infrastructure cost, and how fresh your data can be.

Most developers should be familiar at some point of their career with the four horsemen of the rendering acronyms, but once in a while (and more frequent than not) the nuances and trade-offs escaped us and only click once you see the actual request flows side by side.


The core question

Before we get into each strategy, letโ€™s us ourselves: What problem are we actually solving?

When a user visits a URL, their browser needs HTML to display. The question every rendering strategy answers is: who builds that HTML, and when?

  • CSR: The browser builds it, on every visit
  • SSR: The server builds it, on every request
  • SSG: A build process builds it, once at deploy time
  • ISR: A build process builds it, then quietly rebuilds specific pages in the background
  • Islands: A build process builds it statically, then only the interactive parts get JavaScript

Thatโ€™s the whole map. Everything else is trade-offs.


Client-Side Rendering (CSR)

The server sends an almost empty HTML file (usually called index.html) e.g. a <div id="app"></div> and a <script> tag. The browser then downloads the JavaScript bundle, runs it, and your framework builds the page from scratch in the browser.

src/App.vue
<script setup>
import HelloWorld from './components/HelloWorld.vue'
</script>

<template>
  <HelloWorld />
</template>
src/main.js
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'

createApp(App).mount('#app')

Nothing is visible until the JS finishes, hence the first navigation will be slow. Every navigation after the first is fast because itโ€™s just JavaScript swapping components with no server round-trips needed.

Client-Side Rendering (CSR)Browser requests the page, server returns an empty HTML shell plus a JS bundle from CDN, browser runs the JS and builds the entire page itself.Browserruns JS, builds DOMServerempty shell + JS bundleCDNJS bundle, assetsGET /pageempty HTMLJS bundleblankJS loadinginteractiveRendering happens entirely in the browser. Server does no HTML generation at runtime.

When to use it: Internal dashboards, admin tools, SaaS apps behind a login. Anywhere SEO doesnโ€™t matter and the UI is deeply interactive.

The gotcha: That initial blank screen. On slow connections or low-end devices, users stare at nothing while the JS bundle downloads and executes. This tanks your LCP.


Server-Side Rendering (SSR)

For every request, the server runs your framework, fetches data, renders the full HTML, and ships it to the browser. The user sees real content almost immediately, before any JavaScript runs.

After the HTML arrives, the JS bundle still loads and hydrates the page, attaching event listeners and reactive state. So you can read the content before JS is ready, just that you canโ€™t interact with it yet.

Server-Side Rendering (SSR)Browser sends GET request. Server fetches data from the database, runs the framework, renders complete HTML, and returns it. Browser displays content immediately. JS then hydrates the page to make it interactive.Browserfull HTML, readable nowJS hydrates afterServer1. receive request2. fetch data3. render full HTMLon every requestDatabase / APIlive, per-user dataGET /pagefull HTML pagewaitreadableJS hydratesinteractiveServer renders HTML on every request โ€” great SEO and personalisation, higher infrastructure cost.

When to use it: E-commerce product pages, news sites, anything with SEO requirements and dynamic or personalised data. If content changes per-user or per-request, SSR is your only real option among server-rendered strategies.

The gotcha: Youโ€™re paying for server compute on every single request. Under heavy load this gets expensive real fast. Plus, you still need to handle hydration on the client side.


Static Site Generation (SSG)

At build time i.e. before any user visits, your framework fetches all the data it needs and renders everything for every page into static HTML files. Those files are deployed to a CDN. When someone requests a page, the CDN just serves the file. No server renders anything at runtime.

Static Site Generation (SSG)At build time the build process fetches all data and renders every page to static HTML files, then deploys them to a CDN. At request time, the browser asks the CDN for a page and receives the pre-built file instantly with no server involved.Build time (once at deploy)Request time (every visit)Build processfetch all datarender all pagesdeployCDNpre-built filesglobally cachedGET /pagepre-built file, instantlyBrowserno server renderinstant displayvisible and interactive immediatelyContent is frozen at the last build. Best for blogs, docs, marketing pages. Not suitable for per-user data.

When to use it: Blogs (like this one), documentation, marketing and landing pages, portfolios. Anything where the content is the same for every visitor and doesnโ€™t change by the minute.

The gotcha: Content is frozen at the last build. If your CMS article updates, visitors see the old version until you redeploy. And if you have thousands of pages, build time scales linearly.


Incremental Static Regeneration (ISR)

โ€œWhat if I want SSG but with occasional content updates?โ€

ISR is basically SSG with a safety valve. Pages are still pre-built and served from a CDN, but you set a time to live (TTL). After that window expires, the next request triggers a background rebuild of just that page. The current visitor still gets the stale cached page immediately but the next visitor gets the freshly rebuilt one.

Incremental Static Regeneration (ISR)CDN serves a cached pre-built page instantly. When the TTL expires, the next request triggers a background rebuild of just that page. The current visitor gets the cached version unaffected. The next visitor receives the freshly rebuilt page, also instantly.CDN cachestores page.htmlTTL: 60 secondsCurrent visitorgets page instantlymay be up to 60s oldGET /pageNext visitorgets fresh pagealso instantlyWhen TTL expires, next request triggers a silent background rebuild of only that page:Background regenfetch data โ†’ re-render โ†’ update CDN cacheStatic performance + reasonably fresh content. Cannot personalise per user or serve real-time data.

When to use it: Product pages where prices or stock change periodically but not every second. News articles. Any large site where a full rebuild on every content change is impractical.

The gotcha: Stale content is still possible within the TTL window. And like SSG, you canโ€™t personalise per user.


WTF is Hydration?

Not sure about you but hydration is one of those tech jargons that gets thrown around a lot but I almost always find myself having a hard time grasping it.

For a start, hydration isnโ€™t a rendering strategy, but it comes up whenever you use SSR or SSG. When the server sends HTML to the browser, the page looks correct but is static i.e. no event listeners, no reactivity. The JavaScript framework then loads and hydrates the page: it walks the existing DOM, matches it against the component tree it would have built, and attaches all the event handlers and reactive state without tearing down and rebuilding the HTML from scratch.

Hydration โ€” making server-rendered HTML interactiveThree stages: server HTML arrives visible but static, the JS framework loads and walks the existing DOM attaching listeners and reactivity, the page becomes fully interactive. A warning box below explains what a hydration mismatch is and its common causes.HTML arrivesvisible, readableno listeners, not interactiveJS loadsFramework hydrateswalks existing DOMattaches listeners + reactivityFully interactiveclicks work, reactiveno layout flashThe framework reuses the server HTML without tearing it down โ€” this prevents a visible flash on load.Hydration mismatchServer HTML differs from what the client renders โ€” framework re-renders from scratch, causing a flash.Causes: Date.now() in render, accessing window outside onMounted, client-only conditional content.

Why mismatches happen

If youโ€™re a frontend developer, chances are you might encounter hydration mismatch warning from your console that looks like this:

Vue hydration mismatch warning

A mismatch occurs when the HTML the server sent doesnโ€™t match what the client-side framework would render on its first run. The framework falls back to a full client-side re-render โ€” which defeats the point of SSR entirely, and often causes a visible layout flash.

The usual culprits:

  • Non-deterministic values in render โ€” Date.now(), Math.random(). They return different values on the server and on the client.
  • Browser-only APIs accessed during SSR โ€” window, localStorage, document donโ€™t exist in Node.js. Access these inside onMounted, which only runs client-side.
  • Content that differs by environment โ€” anything conditionally rendered based on viewport size, browser locale, or client state that doesnโ€™t exist at SSR time.

In Vue/Nuxt, wrapping browser-only content in <ClientOnly> sidesteps the issue entirely.


Partial hydration & islands architecture

Traditional hydration has a problem: it hydrates everything. Even if 90% of your page is static content that will never need an event listener, the framework still loads and walks the entire component tree. Thatโ€™s a lot of JavaScript downloaded and executed for nothing.

The islands architecture flips this. Instead of hydrating the whole page and carving out static parts, you start from zero JavaScript and opt in to interactivity only where you need it. Think of your page as an ocean of static HTML with small, independent โ€œislandsโ€ of interactivity scattered across it โ€” each hydrating on its own, in isolation.

This is exactly how Astro works (and how this site is built), and itโ€™s funny that I just found out about this. Because this is a newly-discovered knowledge, I extracted it into a TIL post which you can read here.


The decision map

SituationStrategyWhy
Internal dashboard / admin toolCSRNo SEO, deeply interactive, behind auth
Marketing or landing pageSSGContent is static, SEO critical, must be fast
Blog or documentation siteSSGArticles rarely change, zero server cost
E-commerce product pagesISR or SSRSEO + reasonably fresh stock/price data
News siteISRArticles update but stale-for-60s is fine
User profile / personalised pageSSR or CSRPer-user data canโ€™t be pre-built
Live data (prices, scores)SSR + client pollingMust be truly real-time

One last thing worth internalising: These strategies arenโ€™t mutually exclusive. Most mature applications mix them and most modern meta-frameworks like Nuxt support this use case which is known as hybrid rendering.

In other words, a SaaS product might use SSG for its marketing site, SSR for its SEO-critical public pages, and CSR for the authenticated dashboard โ€” all in the same codebase, handled by the same framework.

Comparing all four

Rendering strategies โ€” comparison tableCSR, SSR, SSG, and ISR compared across first load, SEO, dynamic data, and server cost with colour-coded ratings.StrategyWhere renderedWhen renderedFirst loadSEODynamic dataServer costCSRBrowserOn every visitSlowPoorFullMinimalSSRServerOn every requestFastGreatFullHighSSGBuild serverAt build timeFastestGreatNoneZeroISRBuild + backgroundBuild + on TTL expiryFastGreatPartialLow

Conclusion

At the end of the day, the choice isnโ€™t philosophical. Itโ€™s merely a function of:

  1. Does this page need SEO?
  2. How fresh does the data need to be, and;
  3. How much server compute am I willing to pay for?

As the Malay adage goes, tepuk dada, tanya selera.