In the App Router, all components are Server Components by default — they render on the server with zero client JavaScript, can directly access backend resources, and pass serializable props to Client Components marked with 'use client' that handle interactivity (state, effects, event handlers).
Render on server only, zero client JS, can be async with direct DB/API access. Cannot use hooks, event handlers, or browser APIs.
Pre-rendered via SSR then hydrated on client. Support hooks, events, browser APIs. Ship JavaScript to the browser.
Push 'use client' as far down the tree as possible — the directive makes everything below it in the import tree client-side.
Props from Server to Client Components must be serializable — no functions, classes, or Dates. Use children prop to compose Server Components inside Client Components.
The Server Component / Client Component model is the most significant architectural change in Next.js. Understanding the boundary between them is essential for building efficient applications.
Every component in the App Router is a Server Component unless you add 'use client'. Server Components:
async — await data fetches, database queries, or API calls directly in the component// This is a Server Component (default)
async function UserProfile({ userId }: { userId: string }) {
const user = await db.users.findById(userId); // Direct DB access
return <div>{user.name}</div>;
}Client Components are marked with 'use client' at the top of the file. They:
'use client';
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}When a file has 'use client', every component imported into that file becomes a Client Component — the directive creates a boundary. Everything below the boundary in the import tree is client-side.
This means: push 'use client' as far down the component tree as possible. Only the interactive parts need to be Client Components — keep data fetching, layout, and static content as Server Components.
Server Components can pass Client Components as children or props:
// ServerLayout.tsx (Server Component)
import { InteractiveWidget } from './InteractiveWidget';
async function ServerLayout() {
const data = await fetchData();
return (
<div>
<h1>{data.title}</h1>
<InteractiveWidget>{/* Server Component children */}</InteractiveWidget>
</div>
);
}Client Components can render Server Components passed via children — but Client Components cannot import and render Server Components directly.
Props passed from Server Components to Client Components must be serializable — no functions, classes, or Date objects. Serializable types: strings, numbers, booleans, arrays, plain objects, null, undefined, Map, Set, and typed arrays.
'use client' on layout or page components — this makes everything below them client-side, losing Server Component benefitsServer Components render on the server with zero client JS — they can be async and access backend resources directly. Client Components ('use client') handle interactivity and ship JS to the browser. Push the 'use client' boundary as low as possible. Props across the boundary must be serializable. Server Components can pass Client Components as children, but Client Components cannot import Server Components directly.
Fun Fact
React Server Components were developed over 3+ years before shipping. The React team published an RFC in December 2020 and demonstrated them in a talk that broke the internet — the idea of components that never ship JavaScript to the client was radical. Next.js 13 (October 2022) was the first production framework to ship Server Components, making them mainstream.