React Server Components (RSC) represent a fundamental shift in how we build React applications. They allow components to run exclusively on the server, eliminating the need to ship their JavaScript to the client.
Why Server Components?
Traditional React applications send all component code to the browser. This means:
- Larger bundle sizes — every library and component adds to what the user downloads
- Slower hydration — the browser must parse and execute all that JavaScript
- Waterfall requests — data fetching often happens after the component mounts
Server Components solve these problems by keeping heavy logic on the server.
How They Work
// This component NEVER ships to the client
async function BlogList() {
const posts = await db.query("SELECT * FROM posts");
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
The key insight: the server renders this to a special format (not HTML) that React can stream to the client and merge into the existing component tree.
Server vs Client Components
| Feature | Server Component | Client Component |
|---|---|---|
| Runs on | Server only | Client (+ server for SSR) |
| Can use hooks | ❌ | ✅ |
| Can access backend | ✅ | ❌ |
| Ships JS to client | ❌ | ✅ |
Can use async/await | ✅ | ❌ (directly) |
Best Practices
- Default to Server Components — only add
"use client"when you need interactivity - Push client boundaries down — keep
"use client"as deep in the tree as possible - Avoid passing functions as props from server to client components
- Use Suspense boundaries to stream content progressively
The Mental Model
Think of Server Components as zero-cost abstractions. They let you import large libraries, query databases, and process data without any impact on your client bundle. The result is faster page loads and better user experiences.
Server Components aren't replacing Client Components — they're complementing them. Use server components for data fetching and static rendering, and client components for interactivity and state.