Glossary

Client-Side Rendering

Client-side rendering (CSR) builds the page UI entirely in the browser using JavaScript. The server sends a minimal HTML shell and a JavaScript bundle; the browser executes the JavaScript to fetch data and render the interface.

Explanation

In a client-side rendered app, the initial HTML from the server is essentially empty — just a div with an id that React or Vue will target, and a script tag loading the application bundle. The browser downloads the JavaScript bundle (often hundreds of kilobytes or megabytes), parses and executes it, the framework initializes, fetches data from an API, and renders the UI. Only then does the user see content. This creates the blank-page-on-load problem: the user stares at nothing (or a loading spinner) while JavaScript executes. The metrics affected are First Contentful Paint (FCP) and Largest Contentful Paint (LCP) — both are slow in CSR apps with large bundles. Google's search crawlers used to struggle with CSR because they saw the empty HTML shell, not the rendered content, though Googlebot now executes JavaScript. CSR shines for: applications behind authentication (SEO doesn't matter, users are already authenticated), highly interactive applications where the full JS bundle is justified (dashboard, editor, game), and applications with real-time updates where the client is already managing state. Examples: Gmail, Google Docs, Figma, Notion — all heavily CSR because interactivity is the product. The trend in 2024-2025 is toward hybrid rendering: frameworks like Next.js, Remix, Nuxt, and SvelteKit support SSR for initial page loads (fast, SEO-friendly) with CSR for client-side navigation (no full page reload when navigating between pages). React Server Components push rendering further to the server while preserving interactive client components.

Code Example

javascript
// CSR: React SPA — all rendering happens in the browser
// index.html (what the server actually sends)
/*
  
    
      
*/ // main.jsx — runs entirely in the browser import React from 'react'; import { createRoot } from 'react-dom/client'; import App from './App'; // React takes over the #root div createRoot(document.getElementById('root')).render(); // Data fetching happens after hydration function UserList() { const [users, setUsers] = React.useState([]); const [loading, setLoading] = React.useState(true); React.useEffect(() => { // This fetch happens in the browser after JS loads fetch('/api/users') .then(r => r.json()) .then(data => { setUsers(data); setLoading(false); }); }, []); if (loading) return
Loading...
; // user sees this return
    {users.map(u =>
  • {u.name}
  • )}
; }

Why It Matters for Engineers

Understanding CSR's limitations explains why Next.js became the dominant React framework: the blank-page problem and SEO gaps of pure CSR drove the need for SSR and SSG. Knowing this history makes you a more informed architectural decision-maker. CSR's data fetching patterns (useEffect, React Query, SWR) are also where many bugs live: race conditions from multiple concurrent fetches, stale state from unmounted components, and waterfall fetches (nested useEffects that create sequential round trips). Understanding the CSR model is the prerequisite to understanding and fixing these bugs.

Learn This In Practice

Go deeper with the full module on Beyond Vibe Code.

Web Development Fundamentals → →