Understanding React Server Components
Deep dive into how RSC changes the way we think about data fetching.
The Paradigm Shift
React Server Components represent the biggest architectural change to React since hooks. They fundamentally alter where your components run and how data flows through your application.
Server Components let you write UI that renders on the server — without sending the component code to the client.
This is not SSR. With traditional server-side rendering, your component code still ships to the browser for hydration. Server Components never reach the client at all.
How It Works
The mental model is straightforward:
- Server Components run only on the server. They can directly access databases, file systems, and internal services.
- Client Components run on both server (for initial HTML) and client. They handle interactivity — state, effects, event handlers.
// This is a Server Component by default in Next.js App Router
async function PostList() {
const posts = await db.posts.findMany({
orderBy: { createdAt: "desc" },
take: 10,
});
return (
<ul>
{posts.map((post) => (
<li key={post.id}>
<PostCard post={post} />
</li>
))}
</ul>
);
}No useEffect. No loading states for the initial fetch. No API routes to build. The data is right there.
The Composition Pattern
The real power emerges when you compose server and client components together:
// Server Component — fetches data
async function Dashboard() {
const analytics = await getAnalytics();
return (
<div>
<h1>Dashboard</h1>
{/* Client Component — handles interactivity */}
<InteractiveChart data={analytics} />
{/* Server Component — no JS shipped */}
<RecentActivity />
</div>
);
}This pattern means you ship JavaScript only for the parts that need it. Everything else is just HTML.
Common Pitfalls
When adopting Server Components, watch out for these mistakes:
- Adding
"use client"everywhere — start without it and only add when you need interactivity - Passing non-serialisable props — server-to-client boundaries can only pass JSON-serialisable data
- Importing server-only code in client components — use the
server-onlypackage to catch this at build time - Over-fetching at the layout level — fetch data as close to where it's used as possible
Performance Wins
The numbers speak for themselves:
| Metric | Traditional | With RSC |
|---|---|---|
| JS Bundle | 245 KB | 89 KB |
| Time to Interactive | 3.2s | 1.1s |
| Largest Contentful Paint | 2.8s | 1.4s |
Less JavaScript means faster pages, better Core Web Vitals, and happier users.
Getting Started
If you're using Next.js 13+ with the App Router, you're already using Server Components. Every component in the app/ directory is a Server Component by default. Embrace that default — you'll write less code and ship faster applications.
Enjoyed this post?
Subscribe to get new posts delivered to your inbox.