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:
js'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:
jsx// Form.jsx 'use client'; import FormField from "./FormField"; export default function Form() { return <FormField />; }
jsx// 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:
jsxexport 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
js'use server'; export async function createUser(data) { await db.user.create({ data }); }
Client usage:
jsx<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
fetchduplication - Less 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:
jsxexport 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
jsx// 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
jsx'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! 🚀
