SSR CSR Islands Architecture
System design for SSR, CSR, and islands — pick rendering per surface, hydrate less, and stream wisely.
- system-design
- interview
- architecture
- ssr
- islands
Scope the problem
Choose where HTML is produced and how much JS hydrates. Senior answers assign rendering modes per surface instead of one slogan for the whole company.
Rendering modes
| Mode | HTML | Interactivity | Best for |
|---|---|---|---|
| CSR | minimal shell | full client render | authenticated apps, dashboards |
| SSR | per request | hydrate | personalized SEO pages |
| SSG | build time | hydrate/partial | docs, marketing |
| ISR | SSG + revalidate | hydrate | catalogs |
| Islands | static HTML | hydrate islands only | content sites with widgets |
| Streaming SSR | chunked HTML | progressive | slow data parts |
Mental model
Request
→ resolve data
→ render HTML (server or edge or build)
→ ship minimal JS
→ hydrate only interactive islands
→ client navigations may CSR thereafter
Islands architecture
Page is mostly static HTML/CSS. Interactive spots are islands:
[ static article body ]
[ island: comments ]
[ island: likes button ]
[ island: search box ]
Each island loads its own JS when needed (eager, visible, idle).
<!-- conceptual -->
<article>...</article>
<div data-island="comments" data-props='{"id":"1"}'></div>
<script type="module" src="/islands/comments.js"></script>
Frameworks: Astro islands, Fresh, partial hydration ideas in React (selective hydrate).
Benefits
- Less main-thread JS → better INP/TBT
- Content readable without JS
- Independent island versioning possible
Costs
- Cross-island state harder
- Duplicate framework runtimes if careless
- Routing model different from SPA
SSR + hydration pitfalls
- Hydration mismatch — server HTML ≠ client first render (
Date.now(),random, locale) - Over-hydration — shipping SPA to show a blog post
- Waterfalls — SSR waits on all data before byte 1 (use streaming)
// bad: pure client-only check during SSR
const width = window.innerWidth;
Use useEffect for browser-only values or render consistent placeholders.
Streaming SSR
Shell (nav, layout) flushes early
→ Suspense boundary waits product data
→ streams product HTML
→ deferred secondary widgets
Improves TTFB perception; coordinate with SEO (ensure critical content included).
Hybrid product map (example e-commerce)
| Surface | Mode |
|---|---|
| Home marketing | SSG/ISR + islands (promo carousel) |
| PDP | SSR/ISR for SEO + client ATC island |
| Cart/checkout | CSR SPA (auth, dynamic) |
| Help docs | SSG |
| Admin | CSR |
Data fetching placement
| Place | Use |
|---|---|
| Build | stable content |
| Server request | user-specific, SEO |
| Client | highly interactive, private |
Avoid double-fetch: serialize server state into HTML (__DATA__) for hydration reuse.
Performance comparison levers
- CSR: great after load; weak first contentful for content sites
- SSR: better first paint of data; server cost
- Islands: minimize JS
- Edge SSR: latency (edge tradeoffs)
Caching interactions
Static/ISR HTML caches well. SSR personalized HTML often private. Islands that fetch client-side can keep HTML cacheable — powerful combo.
Tradeoffs
- SPA DX (shared state, client routing) vs multi-page islands
- Full React SSR vs lighter HTML-first frameworks
- Streaming complexity vs slower complete HTML
- SEO needs force SSR/SSG even if CSR feels easier
Interview close
Map surfaces → pick mode per surface → default to ship less JS via islands/static → SSR only when needed → stream slow parts → avoid hydration mismatches. End with caching implications.
Decision flowchart (say out loud)
Is SEO / first content critical?
yes → SSG/ISR/SSR
no → CSR OK
Is most of the page static?
yes → islands / partial hydration
no → full app hydration maybe fine
Is data personalized + cacheable shell?
→ static shell + client islands for user bits
Migration path from SPA
- Extract marketing routes to SSG
- SSR product pages
- Leave app dashboard CSR
- Introduce islands for heavy widgets on content pages
Don’t rewrite the world in one quarter—render modes can coexist behind one design system.
Hydration cost metric
Track JS KB to interactive and long tasks during hydration. If hydration long-tasks dominate INP, you over-hydrated—split islands or defer non-visible widgets.
Related on this site
- Edge Rendering Tradeoffs
- Client-side Routing at Scale
- Core Web Vitals
- System Design Interview Framework