A compilation of my understanding on different frontend rendering strategies as part of my interview prep
Rendering strategies, explained
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.
TL;DR
- CSR — browser builds everything; slow to start, fast after that, SEO suffers.
- SSR — server builds HTML per request; great SEO, personalised data, higher server cost.
- SSG — everything pre-built at deploy; fastest possible, zero server cost, content is frozen.
- ISR — SSG but pages silently refresh after a TTL; static speed with fresher content, still no per-user data.
- Hydration — not a strategy, but a step: the process of making SSR/SSG HTML interactive by attaching JS to it client-side.
- Islands architecture — start from zero JS; only hydrate the interactive “islands” you explicitly opt into. Static HTML ocean, interactive component islands.
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.
<script setup>
import HelloWorld from './components/HelloWorld.vue'
</script>
<template>
<HelloWorld />
</template>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.
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.
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.
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.
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.
- ISR and Stale While Revalidate (SWR) are easy to conflate. Both serve stale content while regenerating in the background.
The difference: ISR caches HTML at the CDN (Content Delivery Network) level while SWR doesn’t. On platforms like Vercel or Netlify however, the line blurs since they can edge-cache SWR responses too, but ISR is still the faster default.
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.
Why mismatches happen
If you’re a frontend developer, chances are you might encounter hydration mismatch warning from your console that looks like this:
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,documentdon’t exist in Node.js. Access these insideonMounted, 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
| Situation | Strategy | Why |
|---|---|---|
| Internal dashboard / admin tool | CSR | No SEO, deeply interactive, behind auth |
| Marketing or landing page | SSG | Content is static, SEO critical, must be fast |
| Blog or documentation site | SSG | Articles rarely change, zero server cost |
| E-commerce product pages | ISR or SSR | SEO + reasonably fresh stock/price data |
| News site | ISR | Articles update but stale-for-60s is fine |
| User profile / personalised page | SSR or CSR | Per-user data can’t be pre-built |
| Live data (prices, scores) | SSR + client polling | Must 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.
When an application mixes between SSR and CSR specifically, this is known as Universal Rendering (also called isomorphic rendering). It simply means the same code runs on both the server (first request) and the client (subsequent visits). It’s the best of both worlds: fast first paint + SEO from SSR, fast navigation from CSR. Nuxt does this by default.
P.S. The term “universal” refers to the code being environment-agnostic.
Comparing all four
Conclusion
At the end of the day, the choice isn’t philosophical. It’s merely a function of:
- Does this page need SEO?
- How fresh does the data need to be, and;
- How much server compute am I willing to pay for?
As the Malay adage goes, tepuk dada, tanya selera.