The App Router flipped React's default: components now run on the server unless you opt into the client with 'use client'. Most confusion comes from reaching for 'use client' out of habit. Here's a rule you can apply in seconds, plus the boundary mistakes that cause the errors.
The rule
Keep a component on the server by default. Add 'use client' only when the component needs one of these:
- Interactivity —
onClick,onChange, and other event handlers. - State or lifecycle —
useState,useReducer,useEffect. - Browser-only APIs —
window,localStorage,IntersectionObserver. - Client-only libraries — anything that touches the DOM directly.
Everything else — data fetching, reading a database, rendering markup, composing layout — is better on the server. If a component does none of the four things above, it has no reason to ship JavaScript.
Why the default is the server
A Server Component runs once, on the server, and sends HTML with zero component JavaScript to the browser. That means smaller bundles, data fetching next to the database with no exposed keys, and no client-side loading spinner for the first paint.
// Server Component (no directive) — fetches on the server, ships no JS
async function OrderList() {
const orders = await db.orders.findMany() // runs on the server
return <ul>{orders.map((o) => <li key={o.id}>{o.total}</li>)}</ul>
}
There's no useEffect, no loading state, and no API route — the component is the data layer.
Push the boundary down
The most common mistake is marking a whole page 'use client' because one button is interactive. That drags the entire subtree — and its data fetching — to the client. Instead, keep the page on the server and isolate the interactive bit into its own small client component.
// page.tsx — stays a Server Component
import { LikeButton } from "./LikeButton" // the only client piece
export default async function Post({ id }) {
const post = await getPost(id)
return (
<article>
<h1>{post.title}</h1>
<p>{post.body}</p>
<LikeButton postId={post.id} /> {/* 'use client' lives here */}
</article>
)
}
The rule: 'use client' goes on the leaves, not the trunk. Put it as far down the tree as the interactivity actually reaches.
The boundary mistakes that cause errors
- Passing a function as a prop across the boundary — a server component can't hand a client component an
onClick; only serializable props (strings, numbers, plain objects) cross. Define the handler inside the client component. - Using
async/awaitin a client component — client components can't be async. Fetch on the server and pass data down, or use a client data library. - Importing a server component into a client component — not allowed. Pass it as
childreninstead, which composes fine. - Reaching for
windowin a server component — it doesn't exist there. That code belongs behind a'use client'boundary, ideally insideuseEffect.
Internalize one line and most of it disappears: server by default, 'use client' only when the browser is genuinely involved, and put the directive on the smallest component that needs it.
FAQ
Are components server components by default in the App Router?
Yes. In the Next.js App Router every component is a Server Component unless the file (or a file it imports from) starts with the 'use client' directive. Server Components render on the server and ship no JavaScript for themselves.
Can a server component import a client component?
Yes — that's the normal pattern. A server component can render a client component and pass it serializable props. The reverse isn't allowed: a client component can't import a server component, but it can accept one as children.
Does 'use client' mean the component only runs in the browser?
No. A client component still renders once on the server for the initial HTML, then hydrates and runs in the browser. 'use client' marks the boundary where interactivity and browser APIs become available.

