PerformanceReactMedium

Client-side rendering vs server-side rendering

01

The Short Answer

In Client-Side Rendering (CSR), the browser downloads a minimal HTML shell and JavaScript builds the entire page in the browser. In Server-Side Rendering (SSR), the server generates the complete HTML before sending it to the browser. CSR gives faster subsequent navigations; SSR gives faster initial paint and better SEO.

02

The Analogy

Imagine you're visiting a restaurant. There are two ways your meal might be prepared:

👨‍🍳

Server-Side Rendering = Kitchen prepares your meal

The chef (server) cooks everything in the kitchen and brings you a fully plated dish. You can start eating immediately. This is SSR — the server does the work and sends you a complete page.

🥘

Client-Side Rendering = You cook at your table

The waiter brings you raw ingredients and a recipe. You have to cook the meal yourself before you can eat. This is CSR — the browser receives JavaScript and has to build the page before the user sees anything.

This analogy captures the fundamental difference between SSR and CSR in web development. Let me explain how these approaches work, their respective advantages and disadvantages, and when you might choose one over the other.

03

The Rendering Process: What Actually Happens

Client-Side Rendering (CSR)

With client-side rendering, the server sends a minimal HTML document to the browser, often just an empty shell with links to JavaScript files. Then:

  • The browser downloads this minimal HTML document
  • It encounters JavaScript file references and downloads them
  • The JavaScript executes in the browser (the "client")
  • The JavaScript code:
    • Requests data from APIs or services
    • Generates HTML dynamically
    • Injects this HTML into the DOM
    • Handles user interactions and updates the page without full reloads

This approach is the foundation of Single Page Applications (SPAs) built with frameworks like React, Vue, or Angular when used in their default configuration.

The code below shows a typical CSR setup. The server sends an empty HTML shell with just a <div id="root"> and a script tag — there's no real content in the HTML at all. React then takes over in the browser, mounts the App component, triggers a fetch to load data from an API, and only after the data arrives does the user finally see content on screen.

csr-example.tsxtypescript
// index.html — the minimal shell the server sends
// <html>
//   <body>
//     <div id="root"></div>  <!-- Empty! -->
//     <script src="/bundle.js"></script>
//   </body>
// </html>

import { createRoot } from 'react-dom/client';
import App from './App';

const root = createRoot(document.getElementById('root')!);
root.render(<App />);

// App.tsx — fetches data and renders UI in the browser
function App() {
  const [users, setUsers] = useState<User[]>([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch('/api/users')
      .then(res => res.json())
      .then(data => {
        setUsers(data);
        setLoading(false);
      });
  }, []);

  if (loading) return <Spinner />;
  return <UserList users={users} />;
}

Notice the problem: the user sees nothing until the JavaScript downloads, executes, fetches data from the API, and finally renders the UI. That's a lot of steps before anything appears on screen.

Server-Side Rendering (SSR)

With server-side rendering, the web server prepares the complete HTML document before sending it to the browser:

  • The browser requests a page from the server
  • The server fetches any required data (from databases, APIs, etc.)
  • The server runs the React components and generates complete HTML
  • The server sends this fully-rendered HTML to the browser
  • The user sees content immediately — the page is readable right away
  • JavaScript downloads in the background and "hydrates" the page (attaches event handlers)
  • The page becomes fully interactive

Traditional websites built with PHP, Ruby on Rails, or Django typically use this approach, but modern frameworks like Next.js, Nuxt.js, and Remix bring SSR to React and Vue applications.

In the Next.js example below, the component is an async function that runs entirely on the server. The data fetch happens before any HTML is sent to the browser, so by the time the user's browser receives the response, it already contains a fully rendered list of users. No loading spinner, no blank screen — content is visible immediately.

ssr-example.tsxtypescript
// Next.js server component — runs on the server
export default async function UsersPage() {
  // This fetch happens on the server, not in the browser
  const users = await fetch('https://api.example.com/users')
    .then(res => res.json());

  // The server renders this to complete HTML and sends it
  return (
    <main>
      <h1>Users</h1>
      <ul>
        {users.map(user => (
          <li key={user.id}>{user.name} — {user.email}</li>
        ))}
      </ul>
    </main>
  );
}

// The browser receives fully-rendered HTML like:
// <main>
//   <h1>Users</h1>
//   <ul>
//     <li>Sarah — sarah@example.com</li>
//     <li>John — john@example.com</li>
//   </ul>
// </main>
04

Advantages and Disadvantages

AspectCSRSSR
Initial page loadSlow — blank screen until JS executesFast — HTML arrives ready to display
SEOPoor — crawlers see empty HTMLExcellent — crawlers see full content
Subsequent navigationVery fast — no full page reload neededSlower — each navigation hits the server
Server loadMinimal — server just serves static filesHigher — server renders on every request
Time to InteractiveDelayed — must download and execute all JS firstSlightly delayed — must hydrate after HTML arrives
Hosting complexitySimple — any static CDN worksRequires Node.js server or serverless functions
Social sharingBroken — link previews show empty contentWorks — Open Graph tags are in the HTML
05

When to Choose Which

Choose CSR when:

  • Building dashboards or internal tools (SEO doesn't matter)
  • Highly interactive apps with frequent client-side state changes
  • Users are authenticated — content is personalized anyway
  • You want simple, cheap deployment to a CDN
  • The app works like a desktop application (Figma, Google Docs)

Choose SSR when:

  • SEO is critical (blogs, e-commerce, marketing pages)
  • First meaningful paint speed matters for conversion rates
  • Content depends on the request (user agent, cookies, URL params)
  • You need social media link previews (Open Graph, Twitter cards)
  • Users are on slow networks or low-powered devices
06

The Modern Approach: Hybrid Rendering

Modern frameworks like Next.js don't force you to choose one or the other. You can mix rendering strategies on a per-page or even per-component basis:

  • SSR for the initial page load (fast paint + SEO), then CSR for subsequent navigations (SPA-like speed)
  • Static Generation (SSG) for pages that don't change per-request (blogs, docs) — HTML generated at build time
  • Incremental Static Regeneration (ISR) for pages that change occasionally — regenerate in the background
  • Streaming SSR for large pages — send HTML in chunks as it's ready, don't wait for everything

The code below demonstrates how Next.js lets you mix all three strategies in a single application. The About page is statically generated at build time (SSG), the Product page fetches fresh data on every request (SSR), and the AddToCart button is a client component that handles interactive state in the browser (CSR). Each page or component uses whichever strategy fits its needs.

hybrid-nextjs.tsxtypescript
// Static page — generated at build time (SSG)
export default function AboutPage() {
  return <div>About us...</div>;
}

// Server-rendered page — generated on each request (SSR)
export default async function ProductPage({ params }) {
  const product = await getProduct(params.id);
  return <ProductDetail product={product} />;
}

// Client component — interactive parts rendered in browser (CSR)
'use client';
export function AddToCartButton({ productId }) {
  const [added, setAdded] = useState(false);
  return (
    <button onClick={() => { addToCart(productId); setAdded(true); }}>
      {added ? 'Added ✓' : 'Add to Cart'}
    </button>
  );
}

The best of both worlds

In Next.js App Router, server components (SSR) are the default. You only add 'use client' to components that need interactivity. This gives you SSR's SEO and performance benefits while keeping interactive parts client-rendered.

07

What is Hydration?

Hydration is the process that bridges SSR and interactivity. When the server sends HTML, it's static — buttons don't work, forms don't submit, nothing is interactive. Hydration is when React "attaches" to the existing HTML and makes it interactive:

  • Server sends complete HTML → user sees content immediately
  • Browser downloads the JavaScript bundle in the background
  • React runs and compares its virtual DOM to the existing HTML
  • React attaches event listeners to the existing DOM nodes
  • The page is now fully interactive — buttons work, forms submit, state updates

The gap between "user sees content" and "page becomes interactive" is called the "uncanny valley" of SSR. The page looks ready but doesn't respond to clicks yet. This is why frameworks are investing in techniques like selective hydration and React Server Components to minimize this gap.

08

Why Interviewers Ask This

This question tests your understanding of web architecture trade-offs. Interviewers want to see that you can reason about performance, SEO, user experience, and infrastructure costs — and that you know when each approach is appropriate rather than defaulting to one pattern for everything. Mentioning hydration and hybrid approaches shows you understand the modern landscape.

Quick Revision Cheat Sheet

CSR: Browser builds the page from JS. Fast navigations, poor initial load and SEO.

SSR: Server sends complete HTML. Fast first paint, good SEO, higher server cost.

Hydration: Process of attaching JS event handlers to server-rendered HTML to make it interactive

SSG: HTML generated at build time — SSR benefits without per-request server cost

Modern best practice: Hybrid — SSR by default, CSR for interactive components, SSG for static content