Server Components vs Client Components
The Most Important Concept in the App Router
Every component in the App Router is a Server Component by default. Server Components run only on the server — they can await database calls, read environment variables, and import heavy server-only libraries without any of that code ever reaching the browser. The result is smaller JavaScript bundles and faster load times.
A Client Component is opted in with the "use client" directive at the top of the file. Client Components are the only place you can use React hooks (useState, useEffect), browser APIs (window, localStorage), or event handlers (onClick, onChange).
// Server Component (default) — no directive needed
export default async function ProductList() {
const products = await fetch("https://api.example.com/products").then(
(r) => r.json()
);
return (
<ul>
{products.map((p: { id: string; name: string }) => (
<li key={p.id}>{p.name}</li>
))}
</ul>
);
}"use client";
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>Count: {count}</button>;
}The Rule of Thumb
- Start with a Server Component. Only add
"use client"when you need it. - Server Components can import and render Client Components.
- Client Components cannot import Server Components.
- Push
"use client"to the leaves of your component tree — keep data fetching at the top in Server Components.
The split between Server and Client Components is the key architectural decision in modern Next.js. Getting this right leads to apps that are fast, secure, and easy to reason about.