Empty div, one script. Everything the user sees is built by JavaScript after the page loads. Google will run that JavaScript, but later, on a slower second pass. Everything else that reads your HTML, like link previews in Slack and iMessage, most other bots, anything that just fetches the source, sees an empty page. A slow phone sees a white screen while 300 kB downloads. And there's no server in the picture at all to fetch data before the first paint.
Next.js exists to fix exactly this, and it does so by adding a server.
React draws. Next.js decides everything around the drawing. Specifically, three things.
Folders in app/ are routes. No router config, no route table. The filesystem is the map.
On the server per request, at build time once, or in the browser. Per page, your call.
On the server only (can read the DB, ships no JS), or on the server and then again in the tab (can use state and clicks). The "use client" line is the border.
One slide each. Then the round trip to the database.
Type a URL, or pick one. The file that answers it lights up. [slug] in a folder name means "anything goes here"; the page receives it in params, which since Next 15 is a promise you await. Every layout.tsx on the way down wraps the page.
CSR: browser gets an empty page, downloads JS, renders. SSR: server renders HTML per request; browser shows it, then downloads JS and "hydrates" (attaches the click handlers). SSG: like SSR, but the HTML was made once at build time and cached.
Bet: on a slow phone, which mode shows content first?
SSG, then SSR close behind. Both send real HTML in the first response. CSR can't show anything until the JS arrives. But notice: in every mode the buttons only work after hydration. Fast-to-see and fast-to-click are different clocks.
Those are the older names. In the App Router the build output calls them static (○, made once at build) and dynamic (ƒ, made per request); CSR is what a "use client" component does after hydration.
"use client".Server components run in Node, once, and send finished HTML. They can read the database directly and ship zero JavaScript to the browser. But they can't hold state or handle clicks, because they aren't there when the click happens.
Client components are the React you know, but the name misleads: they run on the server too. Next.js renders them to HTML on the server like everything else, then also ships their JavaScript so they can hydrate and take clicks. "use client" means "also send this to the browser", not "only run this in the browser". Everything a client file imports crosses the border with it.
Click a component to toggle the directive. Then try the two errors you've definitely seen.
The server sent finished HTML, so the page is visible. But it's a photograph: nothing responds. Then the JavaScript for your client components arrives, React renders them in memory, matches the result to the HTML that's already on screen, and attaches the event handlers. Now it's alive.
If what React renders doesn't match what the server sent (a timestamp, window.innerWidth, a random id), you get the "hydration mismatch" warning. It's not a bug in React; it's the photograph and the live view disagreeing.
Server components never reach step 3. That's the whole reason they ship no JS.
Server components can call the database directly. Client components have to ask the server. Forms have to get their data somewhere. This is the round trip.