React Server Components (and Why They're Not Just Fancy SSR)

4 min read

Ever heard someone say “React Server Components are just a better version of server-side rendering”?

Yeah, me too.

And while they sound similar, they’re actually very different under the hood.

Let’s break it down.

TL;DR


Quick Recap: What’s SSR Again?

Server-side rendering (SSR) is when React pre-renders components on your server, sends fully formed HTML to the browser, and then hydrates it with JavaScript to make it interactive.

That’s good. But also… expensive.

The catch is: Every single component—even simple ones like <Button />—gets shipped with JavaScript. That means slower loads, larger bundles, and more hydration.


React Server Components: A Restaurant Metaphor 🍽️

Imagine you’re at a restaurant.

With RSC, you can choose exactly what gets done server-side and what’s handled client-side.


Server vs. Client Components: Side-by-Side

Here’s the server side:

// Server Component
export default async function PostPage() {
  const post = await fetchPost();
  return <div>{post.title}</div>;
}

This runs entirely on your server—zero JS shipped to the client.

Now, the client side:

'use client';

export default function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

This runs in the browser. It’s interactive, but it ships JS and needs hydration.

With RSC, you can mix both: Server where you can, Client where you must.


Streaming: The Real Superpower

Let’s say part of your page is slow — maybe a DB call.

Traditionally, the full page waits.

With RSC + React 18 + Suspense, React streams the fast parts first. The slow ones come later:

<Suspense fallback={<Loading />}>
  <SlowComponent />
</Suspense>

The browser sees <Header />, <Sidebar />, etc. immediately. <SlowComponent /> arrives when ready — no reload.


Behind the Scenes: How Browsers Handle This 🛠️

Modern browsers can parse incoming HTML as it’s streamed.

React leverages its Flight protocol to send serialized component chunks. The browser hydrates selectively—only the interactive parts.

It’s like React saying: “Here’s the structure—I’ll hand over bricks as soon as they’re ready.”


But Wait… What’s the Catch? 🤔

RSCs are awesome, but they’re not friction-free:

1. Not Everything Works in Server Components

No useState, useEffect, or browser globals (window, document) since server components never touch the browser.

// ❌ Error!
export default function MyServerComponent() {
  const [state, setState] = useState(0);
}

Need interactivity? Move that part to a "use client" component.


2. The Ecosystem is Still Catching Up

Many libraries (like chart.js) assume a browser environment. They’ll have to run in client components for now.

Even devtools and test frameworks may not fully support the RSC model yet.


3. Mental Shift Required

You’ll constantly ask:

It takes time to get used to the new paradigm.


4. Debugging Feels Different

No React DevTools visibility for RSCs. Errors show up in your terminal, not your browser.

A bit strange at first.


5. Deployment: Servers Required 🚀

No static export anymore. Platforms like Vercel? handled this automatically but not with something like GitHub Pages

Caching and latency become new priorities to reduce server load


Use RSC—But Keep Your Eyes Open 👀

React Server Components are a big step forward.

They shine when:


In conclusion here is the comparison: SSR vs. RSC:

FeatureSSRRSC
OutputFull HTML + Full HydrationPartial HTML + Selective Hydration
Data FetchingRoute-level (getServerSideProps)Component-level (async/await, fetch)
JS in BrowserAll JSOnly necessary JS
StreamingLimitedBuilt-in (Suspense)
Hydration CostHighMinimal

React Server Components aren’t just improved SSR—they’re a whole new way to think about what runs where and why it needs to run there.

Not perfect, but it’s an exciting way to improve your app experience and you can adjust your approach as you go. ✅