Server Components Without the Hype: A Mental Model That Sticks
RSC isn't 'SSR 2.0' and it isn't magic. Here's the one distinction that makes the whole model click.

React Server Components confused a lot of people, and most of the confusion comes from pattern-matching them onto things we already knew — "oh, it's just SSR," or "it's like getServerSideProps." It isn't, quite. Here's a mental model that actually sticks.
Forget rendering for a second. The real split is where a component's code lives and runs:
- A Server Component runs only on the server. Its code never ships to the browser. It can read your database, touch the filesystem, use secrets — and then it disappears, leaving behind only the UI it produced.
- A Client Component is the React you already know: its code ships to the browser, and it can use state, effects, and event handlers.
That's the whole core idea. Server Components are components whose JavaScript the user never downloads. Everything else follows from that.
Classic SSR renders your entire app to HTML on the server, then ships all the JS to the browser to "hydrate" it — make it interactive. You paid for the HTML and the full bundle.
RSC lets you say: this part of the tree is just display — render it on the server and don't send its code at all. Only the genuinely interactive bits become Client Components and ship their JS. The static 80% of a typical page stops costing you bundle size.
The page's data-fetching and markup rendering cost zero client JS. Only LikeButton — the part that needs to do something on click — gets shipped.
- Server Components can't use
useState/useEffect/event handlers. They don't exist on the client, so there's no "interactive" lifecycle to hook into. If you need interactivity, you've found a Client Component. - You can pass a Server Component into a Client Component as
children, but you can't import one into a client module. Because the moment youimportit into client code, its server-only code would have to ship — defeating the point. "use client"marks a boundary, not a file-by-file choice. Everything imported below that boundary is in client-land too. Put the directive as deep in the tree as you can.
Once you internalize "Server Components are the code that never ships," every one of those rules stops feeling arbitrary and starts feeling obvious.
- Default to Server Components. Make a component a Client Component only when it needs state, effects, browser APIs, or event handlers.
- Push the
"use client"boundary down. A whole page marked client-side throws away the benefit; an interactive leaf node keeps it. - Don't fight it for purely static sites. If there's barely any interactivity, a simpler static-generation setup may be all you need — RSC earns its complexity on data-heavy, partially-interactive apps.
Server Components aren't "SSR with extra steps." They're a way to write parts of your UI whose code never leaves the server — so the browser downloads only the JavaScript that genuinely needs to run there. Hold onto that one sentence and the rest of the model assembles itself.
Thanks for reading! Let's stay connected:
- ⭐ GitHub — follow me and star the projects: github.com/parsajiravand
- 💬 Discord — join the frontend best-practices community: discord.gg/d9KRhuAwQ
- 📸 Instagram — frontend best practices, daily: @bestpractice___
- 💼 LinkedIn — linkedin.com/in/parsa-jiravand
- ✉️ Email (work & contract inquiries): bestpractice2026@gmail.com
Originally published on dev.to