Next.js: Server Components & Client Components
Leeting Yan
A calm, practical guide to understanding what runs on the server, what runs in the browser, and why this distinction matters in modern React development.
Server Components are one of the defining features of modern Next.js.
They represent a shift in how we think about rendering, data fetching, and performance.
But they also introduce new questions:
- What exactly is a Server Component?
- When do I need a Client Component?
- How do they interact?
- What patterns lead to stable, predictable apps?
In this chapter, weβll clarify these ideas β simply, calmly, without hype.
Why Server Components exist
Traditional React renders everything on the client.
But this leads to unavoidable problems:
- Large JavaScript bundles
- Slow initial loads
- Heavy hydration cycles
- Data fetching complexity
- Difficulty integrating with backend systems
Server Components solve these issues by allowing React to run at build time or on the server, generating lightweight HTML and minimizing work in the browser.
The goal is simple:
Move logic to the server when possible. Keep the browser lightweight and fast.
The key idea: components run in different environments
In the App Router:
- Server Components run on the server (default)
- Client Components run in the browser (opt-in)
This leads to a cleaner mental model:
| Feature | Server Component | Client Component |
|---|---|---|
| Runs in browser? | β No | β Yes |
Can use useState, useEffect, refs? |
β No | β Yes |
| Can access DB, filesystem, private APIs? | β Yes | β No |
| Included in JS bundle? | β No | β Yes |
| Best for | UI + data | Interactivity |
You choose client components only when you need interactivity β everything else stays server-side.
Server Components (default)
A simple Server Component looks like this:
export default async function Page() {
const data = await getData();
return <div>{data.title}</div>;
}
Characteristics:
- No
"use client"directive - Runs on the server
- Can fetch data directly
- Not included in JS bundles
- Can be async
- No state or effects
Server Components are ideal for:
- Rendering database-driven UI
- Fetching external API data
- Static or semi-static content
- Layouts
- Lists, feeds, articles
- Pages without interactivity
They keep your app fast and lean.
Client Components ("use client")
Client Components opt into the browser environment:
"use client";
import { useState } from "react";
export default function Counter() {
const [n, setN] = useState(0);
return <button onClick={() => setN(n + 1)}>{n}</button>;
}
Characteristics:
- Runs in browser
- Can use state, effects, context, refs
- Must be bundled
- Cannot directly access database or secrets
- Can import Server Components (but not the reverse)
Use Client Components when you need:
- Buttons, toggles, interactive UI
- Forms with client behavior
- Animations
- Event listeners
- Web APIs (
localStorage,window, etc.)
Client logic becomes the exception, not the default.
How Server and Client Components work together
React allows mixing both types inside the same UI tree.
Example:
<ServerSidebar>
<ClientChart />
<ClientDropdown />
</ServerSidebar>
Important rules:
-
Server Components can import Client Components.
This is the most common pattern. -
Client Components cannot import Server Components.
This prevents server-only code from being bundled. -
Props between server β client must be serializable.
(JSON-compatible) -
Client Components can accept children that are Server Components, but only through special wrappers (e.g.,
<Suspense>).
This allows for elegant composition:
- Server Components provide data
- Client Components handle interactivity
Your app becomes βas client-heavy as necessary β and no more.β
Performance benefits of Server Components
Server Components significantly improve:
1. Initial load times
You ship less JavaScript, since server-only code never reaches the browser.
2. Data loading
Data is fetched directly on the server without waterfalls or client-side delays.
3. Caching
Requests can be memoized automatically by React, improving both speed and efficiency.
4. Security
Server Components can read environment variables, credentials, and databases β safely.
5. Streaming
UI can render progressively as data resolves.
This is why Server Components are the new default.
When to choose each component type
Below is a calm, practical guide:
Use Server Components for:
- Pages that fetch data
- Lists, tables, dashboards
- Layouts and shells
- SEO-heavy content
- Blog posts, docs, articles
- Anything that doesnβt need interactivity
This should be most of your codebase.
Use Client Components for:
- Inputs, forms, and event handlers
- Components with
useState,useEffect,useRef - Widgets, modals, dropdowns
- Realtime UI
- External libraries that require the DOM
Only introduce them when necessary.
Patterns for real applications
Here are practical combinations that reflect real Birdor-style engineering.
Pattern 1: Server-driven page with client widget
app/
dashboard/
page.tsx β Server Component
chart.tsx β Client Component
// page.tsx
import Chart from "./chart";
export default async function Page() {
const stats = await loadStats();
return <Chart data={stats} />;
}
A clean handoff: server loads data β client visualizes it.
Pattern 2: Server layout with client navigation
layout.tsx β Server
nav.tsx β Client (for local state)
Good for global UI:
- Sidebar
- Header with dropdown
- Mobile navigation
Pattern 3: Server page with client-enhanced sections
<ServerContent />
<ClientActions />
<ClientInteractiveTable />
The page stays fast; only interactive regions load JS.
Common pitfalls and how to avoid them
β Overusing Client Components
Fix: Default to server β add "use client" only when needed.
β Mixing server and client boundaries incorrectly
Remember: client β server imports are not allowed.
β Fetching data inside Client Components
Prefer server-side fetching, then pass data as props.
β Using client components for pure layout
Layouts should almost always be server components.
β Trying to serialize complex class instances
Props must be serializable.
Stay mindful of boundaries, and your app stays predictable.
Summary
Server Components and Client Components give you a powerful way to design fast, modern web applications:
- Server Components β lean, secure, data-ready, fast
- Client Components β interactive, stateful, browser-driven
Use Server Components by default.
Add Client Components only when the UI truly needs them.
This balance keeps your app calm, maintainable, and production-ready.
Next, weβll explore data fetching in Next.js β one of the most important topics in the App Router era.
In many ways, itβs the chapter that unlocks the whole system.