React Server Components Explained (Without the Hype)
Server Components get talked about like they're magic. They're not — they solve one specific problem: sending less JavaScript to the browser. Here's what's actually happening under the hood.
SSR vs Server Components
Traditional server-side rendering still ships the full component code to the client so it can "hydrate" — attach event listeners and re-render. Server Components skip that step entirely for components that never need to run in the browser. Their output is HTML plus a serialized description of the tree; the component code itself never crosses the wire.
What You Gain
- Smaller client JavaScript bundles — server-only components (data fetching, formatting, markdown rendering) add zero bytes to the bundle
- Direct backend access — a server component can query a database or read a file system directly, no API route needed
- Automatic code splitting at the server/client boundary
The Rule That Matters
// Server Component — no hooks, no events, can be async
export default async function ProductList() {
const products = await db.query('SELECT * FROM products');
return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
}
// Client Component — needs interactivity
'use client';
export default function AddToCartButton({ id }) {
const [loading, setLoading] = useState(false);
return <button onClick={() => setLoading(true)}>Add to cart</button>;
}
Common Misconception
Server Components don't replace client-side state management, they complement it. A server component can render a client component and pass it data as props — the boundary is about where code runs, not about giving up interactivity.