Server Components are the biggest paradigm shift in React since hooks. Understanding the mental model is critical.
The Core Idea
Some components only need to run on the server. They can access databases directly, use server-only APIs, and their code never ships to the client.
// Server Component (default in Next.js App Router)
async function UserProfile({ userId }) {
const user = await db.query("SELECT * FROM users WHERE id = $1", [userId]);
return (
<div>
<h1>{user.name}</h1>
<p>{user.bio}</p>
<LikeButton userId={userId} /> {/* Client Component */}
</div>
);
}Server vs Client Components
| Feature | Server | Client |
|---|---|---|
| Render location | Server only | Both (SSR + client) |
| Bundle impact | Zero JS sent | Adds to bundle |
| State/Effects | No useState/useEffect | Full interactivity |
| Data fetching | Direct DB/API access | Client-side fetch |
| Event handlers | None | onClick, onChange, etc. |
The Boundary Rule
Server Components can import Client Components, but NOT vice versa. Think of it as a one-way door:
Server Component
└── can render Client Component ✅
└── can render Server Component ✅
Client Component
└── can render Client Component ✅
└── CANNOT import Server Component ❌
└── CAN receive Server Component as children prop ✅The Children Pattern
// Server Component
function Page() {
return (
<ClientWrapper>
<ServerContent /> {/* This works! Passed as children */}
</ClientWrapper>
);
}
// Client Component
"use client";
function ClientWrapper({ children }) {
const [show, setShow] = useState(true);
return show ? children : null;
}When to Use Client Components
- Interactive UI (forms, buttons, dropdowns)
- Browser APIs (localStorage, geolocation)
- State management (useState, useReducer)
- Effects (useEffect, event listeners)
- Custom hooks that use state or effects