Skip to main content
October 5, 2026/2 min read

React Server Components Explained: What Actually Runs Where

"It's confusing" usually means one thing: nobody drew the line between server and client for you. Here it is.

zanaen
Muhammad Zanaen Ullah
Senior Engineer
React Server Components Explained: What Actually Runs Where

React Server Components changed what "a React component" even means, and most of the confusion around them comes from one unstated assumption carried over from the old model: that every component runs in the browser. It doesn't, and knowing exactly where each one runs is the whole mental model.

Server Components: the default, and they never ship JS

Every component in the App Router is a Server Component unless marked otherwise. It renders once, on the server, has direct access to your database, filesystem, and secrets, and its output is HTML plus a serialized description of what to hydrate — the component's own code never reaches the browser. This is the actual performance win: a data-heavy dashboard component that used to ship its query logic and a data-fetching library to the client now ships neither.

Client Components: opt-in, and the boundary is a one-way door

"use client" at the top of a file marks it and everything it imports as client code — interactive, stateful, running in the browser like classic React always did. The boundary only goes one direction: a Server Component can render a Client Component, but a Client Component can only import other Client Components, never reach back into server-only code.

The mistake that shows up in every codebase learning this

Marking a component "use client" because it needs useState or an onClick handler, without noticing that pulls its entire subtree — including every child it renders — into client-side JS. The fix is usually to push the client boundary as low as possible: a page can stay a Server Component with one small interactive button as a Client Component, instead of making the whole page client-side because of one button.

Streaming and Suspense are what makes this actually fast

Server Components can stream: the shell of a page renders immediately, and slower data-dependent sections stream in behind a Suspense boundary as they resolve, instead of the whole page blocking on the slowest query. This is why a genuinely well-structured RSC page feels faster than the equivalent client-fetched page, even though more work happens on the server.

What we've actually run into

The projects that struggle with RSC almost always have one thing in common: they never actually decided where the server/client boundary should live, so it ended up wherever the first error message pointed. Draw the line deliberately, at the actual point where interactivity is needed — not one component higher than necessary.

Share