Browser or server—who renders now?
React 19 marks one of the framework’s biggest architectural shifts.
Server Components (RSC) are officially stable. Some components render entirely on the server and never ship client-side JavaScript.
Sounds like magic, right? ✨
It’s actually the web’s natural evolution. Let’s unpack it.
Classic model (Client Components)
Historically, React was browser-first. Every component shipped in the JS bundle and became interactive after hydration.function Hello() {
return <h1>Hello Client!</h1>
}
Even if HTML was server-rendered, the component still needed to load JS in the browser. HTML + JS = a React page.⚠️ Problem: bundles grow huge on large apps; startup slows.
New model: Server Components (RSC)
With React 19, some components render only on the server and send HTML output to the client. They don’t add to the browser bundle.// Server Component
export default async function Posts() {
const posts = await fetch('https://api.example.com/posts').then(r => r.json());
return (
<ul>
{posts.map(post => <li key={post.id}>{post.title}</li>)}
</ul>
);
}
This ships as HTML—no client JS to hydrate.Traits of a Server Component
| Property | Meaning |
|---|---|
| Execution | Server |
| Ships JS to client? | No |
| Can fetch data? | Yes (fetch/DB/API) |
| Hooks allowed | Only non-interactive ones (no useEffect/useState) |
| Re-render | Triggered server-side as needed |
Mixing Client & Server Components
React 19 lets you combine both.// 📁 app/page.jsx
import Posts from './Posts.server';
import LikeButton from './LikeButton.client';
export default function Page() {
return (
<main>
<Posts />
<LikeButton />
</main>
);
}
Posts.server→ Server Component (fetches, returns HTML).LikeButton.client→ Client Component (interactivity).
How RSC works
Server Components use React’s Flight protocol. The server sends a special JSON-like stream describing the render. The client resolves it into HTML and stitches it into the tree.Benefits:
- Smaller JS bundles
- Less client rendering
- A better SSR–SPA balance
Real-world example (Next.js 15)
In Next.js 15, Server Components are the default in
app/.// app/posts/page.jsx
export default async function Page() {
const posts = await getPosts();
return (
<>
<h1>Latest Posts</h1>
<ul>
{posts.map(p => <li key={p.id}>{p.title}</li>)}
</ul>
</>
);
}
These render server-side without sending JS. Interactive parts are marked with use client:'use client'
export function LikeButton() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>❤️ {count}</button>;
}
Result: the page may ship ~3 KB of JS—only for the button.
Pros & watch-outs
✅ Advantages
- Dramatically smaller JS bundles
- Faster data access on the server
- Better security (keys stay server-side)
- Excellent SEO (HTML is complete)
Considerations
useEffect/useStatearen’t available in server components.- Share data with client components via props.
- Pay attention to cache strategy.
Data flow with RSC
Server components are async; fetch inside the component—nouseEffect needed.export default async function ProductList() {
const products = await fetch('/api/products').then(r => r.json());
return <div>{products.map(p => <Product key={p.id} {...p} />)}</div>;
}
Fetching and rendering become one step, reducing the need for client-side data libs.Performance impact
- ~30% faster TTFB on average
- Up to 40% smaller bundles
- Shorter hydration = lower CPU
More in React 19
use()hook → await async data directly in components- Actions API → handle form submits on the server
Suspenseintegrates tightly with data fetching
export default function Page() {
const data = use(fetchData());
return <div>{data.title}</div>;
}
Conclusion
React 19’s Server Components blur the line between front end and back end. Less JavaScript reaches the browser; pages get faster; the developer workflow simplifies.In short:
- Server Components = less JS, more speed
- Client Components = interactivity and animation
- Using both together = the future of the web