DG
DevGuide
Run Code
Home/Next.js/Server vs Client Components
N Next.js2 / 3

Server vs Client Components

📌 WHAT

Server Components run only on the server — their code never reaches the browser. Client Components run in the browser and support hooks, events, and interactivity. Server is the default.

💡 WHY

Server components reduce bundle size, improve performance, and allow direct database access. Client components enable interactive features. Using both optimally is core to modern Next.js.

⏰ WHEN / WHERE

Default to Server Components. Add 'use client' ONLY when you need: useState, useEffect, event handlers, browser APIs, or third-party libraries that use hooks.

🔧 HOW

Nothing for Server Components (default). Add 'use client' at the very top of the file for Client Components.

Code Examples

#1Server vs Client Components

Server vs Client Components
tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// =============================================
// SERVER COMPONENT (default — no directive needed)
// =============================================
// ✅ Can: access databases directly, fetch data, use env vars
// ❌ Cannot: useState, useEffect, event handlers, browser APIs
// app/users/page.tsx
import { db } from "@/lib/database"; // direct DB import!
async function UsersPage() {
// Fetch data DIRECTLY in the component — no useEffect needed!
const users = await db.query("SELECT * FROM users LIMIT 10");
return (
<div>
<h1>Users</h1>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}
export default UsersPage;
// =============================================
// CLIENT COMPONENT — needs "use client"
// =============================================
// ✅ Can: useState, useEffect, onClick, onChange, browser APIs
// ❌ Cannot: direct DB access, secret env vars
"use client"; // MUST be first line!
import { useState } from "react";
function SearchBar({ onSearch }: { onSearch: (q: string) => void }) {
const [query, setQuery] = useState("");
return (
<input
value={query}
onChange={(e) => {
setQuery(e.target.value);
onSearch(e.target.value);
}}
placeholder="Search..."
/>
);
}
// =============================================
// PATTERN: Server fetches, Client handles interaction
// =============================================
// app/products/page.tsx (Server Component)
async function ProductsPage() {
const products = await fetch("/api/products").then(r => r.json());
return (
<div>
<SearchBar onSearch={/* ... */} /> {/* Client component inside Server */}
<ProductList products={products} /> {/* Can be Server if no interactivity */}
</div>
);
}

Practice This Concept

Open the playground, paste any example, and experiment. The best way to learn is by breaking things.

Open in Playground