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.
Related Terms
Server-Side Rendering · Single Page Application · DOM · Promise
Learn This In Practice
Go deeper with the full module on Beyond Vibe Code.
Web Development Fundamentals → →