React Server Components (RSC) are one of the biggest shifts in the React ecosystem — and they’ve confused even experienced developers. For years, we thought React components always ran in the browser. But with RSC, that rule changed overnight.
This guide breaks down why RSC feels so different, what new mental models you need, and how to use them correctly without breaking your app.
🧠 1. The Old React Mental Model Is Gone
Before RSC:
“All components run in the browser.”
With RSC:
“Some components run ONLY on the server.”
This suddenly breaks your expectations:
Not all components can use hooks
Not all components can access browser APIs
Not all components can handle events
Not all components run in the client
This is why many developers feel like React is behaving differently now.
🔄 2. You Now Have Two Reacts: Server & Client
Server Components (default)
Run on the server
Can fetch data directly
Don’t ship JavaScript to the browser
Faster by default
Cannot use
useState,useEffect,onClick
Client Components
Run in the browser
Can use hooks
Can handle events
Heavier → increase bundle size
A confusing rule for many:
A client component can import a server component,
but a server component cannot import a client component.
✨ 3. The “use client” Directive and Why It Feels Like Magic
One line changes the entire file:
'use client';Without it:
Hooks won’t work
DOM APIs fail
Event handlers break
With it:
Whole file becomes a client component
All imported components also become client-side
One string can accidentally increase bundle size dramatically.
🔗 4. The Chain Reaction Problem (The RSC Spread Effect)
Example:
// Form.jsx
'use client';
import FormField from "./FormField";
export default function Form() {
return <FormField />;
}// FormField.jsx
export default function FormField() {
return <input />;
}Even though FormField does not use hooks, it becomes a client component because it was imported into one.
This “spread effect” is one of the reasons RSC feels confusing.
⏳ 5. Async Components — A New Way of Thinking
RSC allows asynchronous components:
export default async function Page() {
const user = await getUser();
return <div>{user.name}</div>;
}Before:
Components were always synchronous
Data fetching lived inside
useEffect
Now:
Components can await data directly
No need for extra API routes
This is a major mindset shift.
📡 6. Server Actions — The Most Magical Feature
'use server';
export async function createUser(data) {
await db.user.create({ data });
}Client usage:
<form action={createUser}>
<input name="email" />
<button type="submit">Create</button>
</form>It feels like functions “run on both sides”, but behind the scenes it’s a controlled RPC call.
🚀 7. Why RSC Exists (The Real Benefits)
Faster loading
Smaller JS bundles
Direct database access
Streaming by default
No global
fetchduplicationLess client-side complexity
RSC exists because SPAs became too heavy.
🟩 8. When You SHOULD Use Server Components
Use RSC for:
Data fetching
Static UI
Lists, tables, cards
Product pages
Dashboards
Example:
export default async function ProductsPage() {
const products = await db.product.findMany();
return <ProductList items={products} />;
}🟥 9. When You SHOULD NOT Use Server Components
Avoid RSC for:
Inputs
Buttons
Modals
Forms
Search bars
Interactions
Animations
These require client components.
🧭 10. The New Mental Model You Need in 2025
Adopt this rule:
Start with a Server Component.
Only convert to a Client Component when necessary.
Server = Data + Rendering
Client = State + Interactivity
This simplifies everything.
🔥 Bonus: RSC vs Next.js — Key Differences Developers Miss
In React (Standalone)
RSC is “theory only”
You need a custom bundler
No built-in router integration
Not production-ready without Next.js
In Next.js (App Router)
RSC works out of the box
Streaming is automatic
Layouts work hierarchically
Server/Client boundaries enforced
Next.js is currently the only full RSC implementation.
🧩 Code Example — RSC vs Client Component
Server Component Example
// app/page.jsx
export default async function Page() {
const posts = await fetch("https://api.example.com/posts").then(res => res.json());
return <PostsList posts={posts} />;
}Client Component Example
'use client';
import { useState } from "react";
export default function SearchBox() {
const [query, setQuery] = useState("");
return (
<input
value={query}
onChange={e => setQuery(e.target.value)}
/>
);
}✅ Final Thoughts
React Server Components are not difficult — they are simply new.
Once you understand server boundaries, client boundaries, and async rendering, the entire model becomes easier.
Master RSC now — it is the future of React development.
If you enjoyed this guide, check out more tutorials on my blog! 🚀
