Journal — April 12, 2026 · 5 min read

Server Components — What Actually Changed

React Server Components are not SSR with a new name. What actually changed, what dies, what the server-client boundary costs your team, and who should stay on a plain client app with an API.

The most common thing I hear from clients about React Server Components is a version of "so it's server-side rendering again". That mental model is wrong in a way that makes everything downstream confusing, so it's worth correcting before anything else.

The misconception

Server-side rendering means: run your component tree on the server, produce an HTML string, send it, then send the same components as JavaScript to the browser so React can hydrate and take over. The server render is a head start on the first paint. Every component still ships to the client.

Server Components are a different thing. A server component runs on the server and its code never reaches the browser at all. Not deferred, not lazy-loaded. It isn't in the bundle. What crosses the wire is a serialised description of the rendered output, which the client React runtime stitches into the tree.

SSR is an optimisation of when rendering happens. RSC is a change to where code lives. You can have either without the other, and Next.js shipping both together is a large part of why people conflate them.

What actually changed

Components that cost nothing on the client. Your markdown renderer, your syntax highlighter, your date formatting library with all its locale data: if they only run on the server, the user downloads none of it. That's the concrete win, and on content-heavy sites it's substantial in a way no amount of code-splitting achieved.

The boundary became an explicit architectural seam. Previously "what runs where" was implicit and mostly accidental. Now "use client" draws a line you can see in the source tree. That's genuinely better design, and it's also the source of every complaint below.

Data fetching moved into the component. Instead of a loader above the tree passing props down, a server component just awaits its own data. Colocation is real: the thing that needs the query owns the query, and deleting the component deletes the query. It also makes waterfalls easier to create by accident, which is the trade.

A lot of things die. Most client state that existed only to hold fetched data goes away. So does most of your caching layer for that data. And so do the API routes that existed purely to feed your own frontend, which in some codebases is a third of the backend surface. That layer was always a serialisation tax you paid to talk to yourself.

The honest costs

Everyone on the team must understand the boundary. This is not optional knowledge that seniors hold for juniors. Every person writing a component needs to know which side they're on, what serialises across, and why they can't pass a function as a prop into a server component. The first month with a team new to this is slow.

"use client" spreads upward. You mark a leaf component as client because it needs useState. Then its parent must pass it props that serialise. Then someone needs a context provider, which is a client component, and it wraps half the tree. The client boundary drifts up until most of your app is on the client side of the line and you're back where you started with extra ceremony. Keeping it low requires deliberate, ongoing effort: push state into small leaves, pass server components as children rather than importing them into client components.

Third-party libraries assume a client. Most React libraries were written for a world where everything is a client component. Many now ship the directive; plenty don't, and you end up writing thin "use client" wrappers around them. This has improved a lot but it hasn't stopped being an occasional afternoon.

Caching semantics confused everyone, including the framework authors. Next.js's caching defaults changed materially across versions, and the fact that they changed is the tell. I've had to explain to a client why a page showed stale data after a deploy, and the honest answer was that four caching layers were involved and I'd assumed the wrong default in one of them. That was my mistake, and it also wasn't a mistake I would have been able to make in 2020.

Debugging across the seam is worse. A server component error shows in your terminal, a client one in the browser console, and an error thrown during serialisation of a prop between them shows up as neither of those things in a form anyone enjoys reading. Stack traces got less useful.

Who should adopt

Adopt if your product is content-heavy and read-dominant. Marketing sites, docs, e-commerce catalogues, dashboards that are mostly display. This is what RSC is genuinely good at: less JavaScript on the wire, data next to the markup, fast first render. If SEO or first-load performance is part of the business case, the argument makes itself.

Adopt if you're already on the Next.js App Router, which for most new products is the default I'd pick anyway (my reasoning here). You're paying the cost regardless. Learn the model properly instead of fighting it with "use client" at the top of every file, which is the pattern I see most often and which gets you all of the complexity and none of the benefit.

Stay on a plain client app plus an API if your product is an application, not a document. A canvas editor, a trading terminal, a real-time collaboration tool, anything where nearly everything is interactive and stateful. You will mark almost every component as client, and then you have added a boundary you don't use. Vite plus React plus a well-designed API is still an excellent stack and I still start projects there, for reasons I've laid out in why I reach for boring technology.

Stay put if you have a mobile app consuming the same API. Your API routes aren't redundant, they're a product surface with a second consumer. Half of RSC's savings evaporate.

The framing I've settled on with clients: Server Components didn't make React faster, they made the network boundary a design decision instead of an accident. If your product has a meaningful amount of code that shouldn't be on the client, that's worth the ceremony. If it doesn't, you've bought a seam you'll spend the next two years explaining to new hires.