Server-Side Rendering (SSR)
The biggest architectural decision in web development: who builds the HTML that the user sees?
The server generates complete HTML for every request. The browser receives a finished page and just displays it.
Client Server │ │ │──── GET /products ────────────────>│ │ │ Server queries DB, │ │ builds HTML with data, │ │ sends complete page │<──── <html><body>...products... │ │ │ │ Browser displays HTML immediately │ │ No JavaScript needed to see content
Client-Side Rendering (CSR)
The server sends a minimal HTML shell plus a large JavaScript bundle. The JavaScript fetches data via API and builds the page in the browser.
Client Server
│ │
│──── GET /products ────────────────>│
│<──── <html><div id="root"></div> │ Nearly empty HTML
│ + app.js (2MB) │
│ │
│ JS loads, initializes framework │
│ │
│──── GET /api/products ────────────>│
│<──── {"products": [...]} │ JSON data
│ │
│ JS builds DOM, renders page │
│ User finally sees content │
Comparing the Approaches
| Aspect | SSR | CSR | Hybrid |
|---|---|---|---|
| First page load | Fast (HTML ready) | Slow (JS must load + execute + fetch) | Fast (HTML ready, JS enhances) |
| Subsequent navigation | Slow (full page reload) | Fast (client-side routing) | Can be fast (progressive loading) |
| SEO | Excellent | Poor (without SSR pre-rendering) | Excellent |
| Works without JS | Yes | No | Yes (degraded gracefully) |
| Server load | Higher (renders every page) | Lower (serves static files + API) | Moderate |
| Complexity | Low | High (state management, routing, build) | Very High |
The Generational Evolution
Web architecture hasn't evolved in a straight line — it has moved through distinct generations, each solving the problems of the previous one while creating new trade-offs. Critically, all generations remain legitimate. The right choice depends on the problem, not the calendar.
| Generation | Era | Approach | Characteristics |
|---|---|---|---|
| Gen 1: Server-Side Focused | ~1993–1999 | Thin client, server renders everything | 1 URL = 1 page. Simple, but every interaction required a full page reload. |
| Gen 1.5: Enhanced Client | ~1997–2004 | Server-side + progressive enhancement | Client-side form validation, DHTML effects. Server still controls flow. |
| Gen 2: Ajax | ~2005–2012 | XMLHttpRequest, in-place updates | Broke 1 URL = 1 resource (hashbang URLs, then History API fixed it). Gmail, Google Maps as pioneers. |
| Gen 3: Native Apps | ~2008–present | iOS/Android, app store distribution | Web views as hybrid compromise. Native performance, but platform lock-in and distribution friction. |
| Gen 4: PWAs | ~2015–present | Offline-first, service workers | Speed of native + web distribution. Install without app stores. Still maturing. |
| Current: SSR + Hydration | ~2018–present | Send static snapshot fast, then hydrate with JS | Best of SSR and CSR. Frameworks like Next.js, Nuxt, Astro. Complexity is the cost. |
Form Follows Function
The appropriate architectural approach reveals itself from the problem, not from trends. The decision process:
- Is this a site or an app? (Tutorial 13) — Content-dominant or interaction-dominant?
- What degree of dynamism does it need? — Static content? Real-time data? User-generated content?
- What's the context? — Audience, constraints, team capabilities, business requirements.
To believe that only the latest generation is valid is to be too wrapped up with form and not function. A static HTML site is the right answer for many problems. A full SPA with SSR and hydration is the right answer for others. The wrong choice in either direction — over-engineering a brochure site or under-engineering a complex app — creates unnecessary pain.
Maturity-Safety Matrix
When evaluating technologies, consider two axes:
- Maturity: How long has it been used in production? How stable is the API? How large is the community?
- Safety: What's the blast radius if it fails? How well understood are the security implications?
Mature, safe technologies (HTML, CSS, server-rendered pages) are boring but reliable. Immature, risky technologies (the latest JS framework) are exciting but may not survive. For production systems, especially those meant to be long-lived, a boring, mature, and stable solution is critical.