09-nextjsTermsLevel_01Client-Side Rendering (CSR) / SPA

Client-Side Rendering (CSR) / SPA

Level 1 — Core Concepts & Architecture A web application architecture where the browser downloads a single empty HTML page and builds the entire user interface dynamically using JavaScript.


1. Prerequisites


2. Term Category

Rendering Strategy (Client-Side Single Page Application Rendering): Client-Side Rendering (CSR) executes component rendering and routing inside the browser DOM after fetching minimal initial HTML.


3. Explanation

Environment Context

  • Client Only (All rendering execution occurs inside the client's web browser).

(1) Design Motivation — "Why did we design this?"

In traditional multi-page web applications, every click on a link triggers a full page reload. The browser destroys the old page, requests a new HTML document from the server, and renders it from scratch. This causes a slow "white flash" screen transition.

Single Page Applications (SPAs) utilizing Client-Side Rendering (CSR) were designed to solve this. Instead of fetching new HTML on every request, the server sends down a single, nearly empty HTML file (typically containing just <div id="root"></div>) along with a large bundle of JavaScript code. The browser runs this JavaScript to render components, fetch data, and rewrite the DOM dynamically on page navigation, making transitions feel fast.


(2) Core Concept — How CSR/SPA Works

When a user visits a CSR website (like standard React/Vite builds):

  1. Initial Fetch: The server responds with a minimal HTML shell:
    <!DOCTYPE html>
    <html>
      <head><title>My CSR React App</title></head>
      <body>
        <div id="root"></div> <!-- Empty Container -->
        <script src="/bundle.js"></script> <!-- Heavy JavaScript -->
      </body>
    </html>
    
  2. Downloading Assets: The browser displays a blank page while downloading the bundle.js script.
  3. Client Render: Once downloaded, the browser executes the JavaScript, queries database APIs, and populates the #root container.

(3) The Core Problems of CSR

While SPAs provide fast transitions after the initial load, they introduce major drawbacks:

  • Poor SEO: Search engine crawl bots (like Google or Bing) read the initial HTML. Because the initial HTML is empty in CSR, bots struggle to index client-rendered content.
  • Slow Time to Interactive (TTI): On slow mobile devices or weak networks, downloading and compiling a massive 5MB JavaScript bundle blocks users from seeing or interacting with the page for seconds.

4. Common Mistakes & Pitfalls

Mistake 1: Expecting CSR apps to have good initial loading speeds on slow devices

The mistake: Building a standard React Vite app with heavy client dependencies, assuming it will load quickly because "transitions are instant."

Why it's wrong: While transitions are fast, the initial page load requires the client to download, parse, and execute all JavaScript before displaying anything. If the bundle is large, users see a blank screen or a spinner for several seconds.

Golden Rule: Use Server-Side Rendering (SSR) or Static Site Generation (SSG) if your app requires fast initial load speeds and search engine optimization.


Mistake 2: Relying on Pure CSR for Public SEO-Driven Next.js Landing Pages

The mistake: Wrapping an entire public landing page in 'use client' with useEffect data fetching.

Why it's wrong: Pure Client-Side Rendering (CSR) serves an empty HTML shell initially. Search engine crawlers receive no text content, hurting SEO rankings. Use Server Components or pre-rendered SSR.

Incorrect:

'use client';
export default function Page() {
  const [data, setData] = useState(null);
  useEffect(() => { fetch('/api/data').then(r => r.json()).then(setData); }, []);
  return <div>{data?.title}</div>; // ❌ Empty initial HTML shell!
}

Fix:

// Server Component pre-renders HTML on the server:
export default async function Page() {
  const res = await fetch('https://api.example.com/data');
  const data = await res.json();
  return <div>{data.title}</div>; // Clean SEO HTML
}

Mistake 3: Experiencing Layout Shift (CLS) from Un-Gated Client Data Fetching

The mistake: Rendering empty containers that pop into view after client useEffect fetch resolves.

Why it's wrong: CSR fetching creates visible Cumulative Layout Shift (CLS) as UI elements pop into existence after client mounting. Use Server Components or Suspense loading skeletons.

Incorrect:

/* CSR component rendering null then jumping when useEffect finishes */

Fix:

/* Use Next.js loading.tsx or Suspense fallback skeletons for smooth layout preservation */

5. Practice Exercises

Exercise 1: Client-Side SPA Navigation and Rendering

Scenario: Configure a Client Component performing client-side data fetching using useEffect().

Requirements:

  1. Use useEffect() and fetch() inside "use client" component.
Answer

Implementation

"use client";

import { useState, useEffect } from "react";

export default function ClientFeed() {
  const [data, setData] = useState<any[]>([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch("/api/feed")
      .then((res) => res.json())
      .then((json) => {
        setData(json);
        setLoading(false);
      });
  }, []);

  if (loading) return <div>Loading Feed...</div>;

  return (
    <ul>
      {data.map((item) => (
        <li key={item.id}>{item.title}</li>
      ))}
    </ul>
  );
}

Technical Explanation

  1. Client-Side Rendering executes rendering and data fetching inside the browser DOM after hydration.
  2. Minimal initial HTML is delivered to the browser; useEffect triggers post-mount network calls.
  3. Useful for private client dashboards requiring client-side interactivity.

Exercise 2: Opting Out of Server Component SSR Rendering

Scenario: Use dynamic imports with { ssr: false } to execute a heavy charting component strictly on the client browser.

Requirements:

  1. Code dynamic(() => import(...), { ssr: false }).
Answer

Implementation

import dynamic from "next/dynamic";

const DynamicChart = dynamic(() => import("@/app/components/HeavyChart"), {
  ssr: false,
  loading: () => <p>Loading Interactive Chart...</p>
});

export default function Dashboard() {
  return (
    <div>
      <h1>Analytics</h1>
      <DynamicChart />
    </div>
  );
}

Technical Explanation

  1. dynamic(..., { ssr: false }) opts out of server-side HTML pre-rendering for the specified component.
  2. Prevents server evaluation of browser-dependent window or canvas libraries.
  3. Renders the fallback component until the client bundle hydrates in the browser.

Exercise 3: Trade-Off Analysis: CSR vs SSR

Scenario: Formulate an architectural selection matrix comparing Client-Side Rendering (CSR) against Server-Side Rendering (SSR).

Requirements:

  1. Contrast SEO, server CPU cost, initial load speed, and browser requirements.
Answer

Implementation

CSR vs SSR Architecture Comparison:
- CSR (Client-Side Rendering): Zero server Node.js rendering CPU cost, poor SEO indexing, slower initial content display (TTFB to FCP delay).
- SSR (Server-Side Rendering): Excellent SEO, instant initial HTML display, higher Node.js server RAM/CPU costs.
Recommendation: Use SSR for public marketing/e-commerce; use CSR for internal admin tools behind login walls.

Technical Explanation

  1. CSR relies on client CPU power to build DOM elements.
  2. SSR pre-computes HTML on Node.js servers for web crawlers and instant initial page paints.
  3. Core architectural selection model.


7. Key Takeaways

  • CSR/SPA apps load a single empty HTML page and populate it using client-side JavaScript.
  • CSR provides fast navigation transitions without full-page reloads.
  • The initial page load is slow because the browser must download and compile all JavaScript.
  • Crawler search bots read empty HTML, making CSR poorly suited for search optimization.
  • Next.js bridges this gap by combining server rendering with client interactivity.
Built with LogoFlowershow