Render Modes

Client-Side Rendering

The server delivers a near-empty shell. The browser does the rest: downloading the JS bundle, mounting React, calling APIs, and painting the real content. Until that's finished, the user watches a spinner.

Server shell built atCSR

2026-05-05T12:37:15.787Z

The build-time stamp here is the shell's prerender timestamp (via cacheLife('max')); pure CSR would have no server-rendered timestamp at all. The shell is reused on every visit, fresh data comes from the client fetch on the right.

Fetched in browserCSR

Loading…

The browser is running JavaScript right now to call /api/now. In a true CSR app this is the only way the page gets its data.

What happens

  1. Build time,Next.js prerenders this page's shell into HTML. No data is fetched.
  2. Request, the CDN serves that shell. The browser parses HTML, kicks off the JS bundle.
  3. Hydrate, React mounts the CsrIsland in the browser.
  4. Fetch, a useEffect calls /api/now from the client.
  5. Render,the response replaces the loading state. Total time depends on the user's network and CPU.

The code

csr/page.tsx
// app/rendering/csr/page.tsx, server shell, mostly static
"use cache";
import { cacheLife } from "next/cache";

export default async function Page() {
  cacheLife("max");
  return <CsrIsland />;
}

// _components/CsrIsland.tsx, runs in the browser
"use client";
import { useEffect, useState } from "react";

export function CsrIsland() {
  const [data, setData] = useState(null);
  useEffect(() => {
    fetch("/api/now").then((r) => r.json()).then(setData);
  }, []);
  return <span>{data?.iso ?? "Loading…"}</span>;
}

Trade-offs

  • Good: simple mental model, fast to deploy, infinite scale on a CDN.
  • Bad:blank screen until JS arrives, weaker SEO out of the box (Googlebot can render JS in a second pass, but other crawlers and OpenGraph previewers usually can't), every device does the work.