Client-Side Rendering (CSR)

The server sends a nearly empty HTML shell and the browser's JavaScript draws the entire page after it loads.

CSRthe JavaScript builds the page after it loadsclient-side rederingblank page until the JavaScript runsthe app builds itself in the browserempty div with a script tagwhite flash before anything shows upthe browser draws the page

See it

Live demo coming soon

What it is

In client-side rendering the server hands over an almost empty HTML file (one 'div id=root' and a script tag) and the browser does the rest: download the JavaScript, run it, fetch the data, build the DOM. This is what a plain Vite or Create React App build does. Do not confuse it with client-side navigation: a router swapping views without a full reload is a navigation model, and the views it swaps in can be built in the browser, streamed from the server, or a mix, depending on the framework.

Reach for it when the content is private, personal, or highly interactive: dashboards, editors, admin panels, anything behind a login. There is no SEO to lose, and a build cannot pre-render a page that depends on who is looking at it. Logged-in pages can still be worth server-rendering, though: it paints sooner on a slow phone and degrades better when the bundle fails, so treat it as a real tradeoff (simpler hosting and simpler code against a faster, sturdier first paint) rather than a rule that private means client-rendered. Navigation after boot feels quick either way, because the router usually avoids fetching a whole new document.

The gotcha is the first paint. Nothing at all is visible until the bundle downloads, parses, and runs, so slow phones and slow networks stare at white. Code-split per route, ship a real skeleton instead of a spinner, and never CSR a marketing page you want ranked: crawlers and link previews often see the empty shell.

Ask AI for it

Build this as a client-side rendered React app with Vite: a single index.html containing an empty root div, React Router handling all views in the browser, and data fetched client-side with TanStack Query. Lazy-load every route with React.lazy plus Suspense so the first bundle stays small, show skeleton placeholders (not spinners) while data loads, and render a fallback message inside the root div for the no-JavaScript case.

You might have meant

server side renderingsingle page apphydrationstatic site generationcode splitting