Server Components vs Client Components — When to Use Each
In this tutorial, you will learn about Server Components vs Client Components. We cover key concepts, practical examples, and best practices to help you master this topic.
React gives you two component types: Server Components that render on the server and never ship JavaScript, and Client Components that run in the browser and support interactivity.
What You'll Learn
You will understand the criteria for choosing between Server and Client Components, how to structure your component tree, and patterns for mixing both types effectively.
Why It Matters
Choosing the wrong component type leads to bloated bundles, slow page loads, or broken interactivity. Understanding the server-client boundary prevents these problems and lets you optimize performance.
Real-World Use
DodaZIP's file manager uses Server Components to render the file tree from the database and Client Components only for the drag-and-drop upload zone and rename buttons.
flowchart TD
A[Page Component] --> B{Is interactivity needed?}
B -->|No| C[Server Component]
B -->|Yes| D[Client Component]
C --> E[Fetch data directly]
C --> F[Render static output]
D --> G[Use hooks/state]
D --> H[Handle user events]
E --> I[Send HTML to client]
F --> I
G --> J[Send JS + HTML]
H --> J
style C fill:#1e293b,color:#fff
style D fill:#0f172a,color:#fff
The Decision Tree
Every React component in the App Router is a Server Component by default. You opt into client rendering by adding 'use client' at the top of a file.
Use Server Components When
The component fetches data from a database or filesystem. The component only displays data without user interaction. The component uses server-only modules like fs, crypto, or database drivers.
async function ProductList() {
const products = await db.query('SELECT * FROM products WHERE active = true');
return (
<ul>
{products.map(p => (
<li key={p.id}>{p.name} — ${p.price}</li>
))}
</ul>
);
}
Expected output: An unordered list of product names and prices rendered as HTML. No JavaScript is sent for this component.
Use Client Components When
The component uses React hooks like useState, useEffect, useReducer. The component handles user events like clicks, form input, or scrolling. The component uses browser-only APIs like localStorage, IntersectionObserver, or navigator.
'use client';
function SearchBar() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
useEffect(() => {
const timer = setTimeout(async () => {
if (query.length > 2) {
const res = await fetch(`/api/search?q=${query}`);
setResults(await res.json());
}
}, 300);
return () => clearTimeout(timer);
}, [query]);
return (
<div>
<input value={query} onChange={e => setQuery(e.target.value)} />
<ul>{results.map(r => <li key={r.id}>{r.title}</li>)}</ul>
</div>
);
}
Expected output: A search input that filters results as the user types, with a 300ms debounce to avoid excessive API calls.
The Boundary Rule
Once you mark a file with 'use client', all components in that file become Client Components. You cannot import Server Components into a Client Component file.
'use client';
// This file is now a Client Component boundary
import ServerComponent from './ServerComponent'; // ERROR
import ClientComponent from './ClientComponent'; // OK
Expected output: A build error telling you that Server Components cannot be imported inside Client Components. Only pass them as children or props.
Common Mistakes
Making the entire page a Client Component: Only wrap the interactive parts. Default to Server Components for the page layout and data fetching sections.
Forgetting that children can be Server Components: Even inside a Client Component, children passed as props can be Server Components. This is the composition pattern.
Putting use client in a parent component unnecessarily: The directive applies to the file. If only one small section needs interactivity, extract it into its own file and mark only that file.
Assuming all third-party components are Server-safe: Many npm packages rely on browser APIs or hooks. Check the package source or wrap them in a Client Component wrapper.
Not splitting components early enough: Identify interactive parts during planning. Refactoring later requires extracting components and managing the boundary.
Practice Questions
- What is the default component type in Next.js App Router?
Every component is a Server Component by default. You opt into client rendering with
'use client'.
- Can you use useEffect in a Server Component?
No. useEffect is a React hook that requires browser runtime. Only Client Components can use hooks.
- How do you pass a Server Component inside a Client Component?
Pass the Server Component as a child or prop. The Client Component renders its children without knowing whether they are Server or Client Components.
- What happens if you import a Server Component into a Client Component file?
React throws a build error. Server Components cannot be imported into files marked with
'use client'.
- When would you refactor a Server Component into a Client Component?
When you need to add interactivity, use hooks, or access browser APIs that are not available on the server.
Challenge
Refactor a page that currently has 'use client' at the top to move the interactive section into a separate Client Component, keeping the page shell and data fetching as Server Components.
Frequently Asked Questions
Mini Project
Build a product listing page with a Server Component fetching products from a database, a Client Component for the shopping cart add button, and another Client Component for a price filter slider.
What's Next
Continue to use client Directive to understand how the boundary marker works in detail.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro