Where is your app rendered?
A React app produces HTML, but where and when that HTML is generated changes performance and SEO a lot. There are three classic strategies.
CSR — Client-Side Rendering
The server sends almost empty HTML and a JavaScript bundle. The browser downloads it, runs React, and only then paints the interface. This is what a traditional SPA does (e.g. with Vite or Create React App).
- ✅ Simple; after the first load, navigation is very smooth.
- ❌ Slow first paint (you have to wait for the JS) and bad SEO: search engines receive an empty page at first.
SSR — Server-Side Rendering
The server runs React on each request and returns the already-painted HTML. The browser shows it immediately and, afterward, React "wakes it up" (a process called hydration) to make it interactive.
- ✅ Complete HTML from the very first moment → better SEO and fast first paint.
- ❌ More load on the server (it renders on each visit).
SSG — Static Site Generation
The HTML is generated only once at build time and served as static files from a CDN. Ideal for content that changes little (blogs, documentation, landing pages).
- ✅ Super fast and cheap (static, cacheable files).
- ❌ Not suitable for highly dynamic or per-user personalized content.
Summary: CSR renders in the browser; SSR on the server per request; SSG at build time. Most modern frameworks let you mix all three depending on the page.
React Server Components (RSC)
Server Components are a more recent evolution: components that run only on the server and never send their JavaScript to the browser. The server renders their output and sends it ready; the client receives less JS.
- They can access the database directly, read files, or use secrets, because they run on the server.
- They cannot use state or effects (
useState,useEffect) or event handlers: that's the client's job.
When a part does need interactivity (a button with state, an input),
it's marked as a Client Component with the directive "use client" at
the top of the file:
"use client";
import { useState } from "react";
export function Counter() {
const [n, setN] = useState(0);
return <button onClick={() => setN(n + 1)}>Clicked {n}</button>;
}
Key idea: by default, in this model components are server ones (zero JS to the client). You add
"use client"only in the "islands" that truly need interactivity. That way you ship less JavaScript.
Next.js and the App Router
Next.js is the most-used React framework for SSR/SSG and Server Components.
Its App Router (the app/ folder) treats every component as server by
default and uses "use client" for interactive islands. Plus, it lets you
choose per page between static rendering (SSG) and dynamic (SSR).
// app/page.tsx → Server Component by default
export default async function Page() {
const data = await getData(); // runs on the server
return <List data={data} />;
}
Why does it matter?
- Performance: the user sees content sooner (server HTML) and downloads less JavaScript (Server Components) → the page is usable as soon as possible.
- SEO: search engines and social networks receive real HTML with the content, not an empty page waiting for JavaScript.
You don't need SSR for everything. An internal app with no SEO can do fine with CSR. The professional approach is to choose the strategy per page based on its needs.