Understand when to use Server Components vs Client Components — with real examples and common pitfalls.
The Mental Model
Server Components render on the server and send HTML to the client — no JavaScript bundle for that component. Client Components render on the server too, but they also hydrate on the client with JavaScript for interactivity.
Default in Next.js App Router: everything is a Server Component unless you add 'use client'.
When to Use Server Components
- Fetching data from a database or API
- Reading files from the filesystem
- Rendering static content (blog posts, product listings)
- Any component that does not need
useState,useEffect, or event handlers
When to Use Client Components
- Interactive UI (forms, modals, dropdowns)
- Components using React hooks (
useState,useEffect,useRef) - Components that need browser APIs (localStorage, window, etc.)
- Event handlers (onClick, onChange, etc.)
The Boundary Pattern
You do not have to make entire pages one type. Use Server Components for the outer layout and data fetching, then nest Client Components for interactive parts:
ServerLayout
├── ServerSidebar (fetches nav items)
├── ServerContent (fetches article)
│ └── ClientCommentForm (interactive)
└── ClientMobileMenu (interactive)
Common Mistakes
- Adding
'use client'to everything "just to be safe" — this defeats the purpose - Trying to use hooks in Server Components — they only work in Client Components
- Passing non-serializable props (functions, classes) from Server to Client Components
- Not understanding that Client Components still render on the server initially (SSR)
Data Fetching Pattern
Server Components can be async and fetch data directly:
async function ProductList() {
const products = await db.query('SELECT * FROM products')
return {products.map(p => - {p.name}
)}
}
No useEffect, no loading states, no client-side fetch waterfall.
When It Clicks
The moment Server Components make sense: you realize that most of your page is static content with small islands of interactivity. Server Components let you treat those differently — zero JS for the static parts, interactive JavaScript only where needed.