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.tsximport { 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 (<inputvalue={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