React
A mental model for React Server Components
Server Components render on the server, Client Components cross a serializable boundary, and the file directive is what marks that line.
- React
- Server Components
- Next.js
On this page
In the App Router, a component is a Server Component unless its file starts with "use client". The directive opts that file, and every module it imports, into the client bundle. It does not mean the component runs only in the browser. Client Components still pre-render on the server, and then they hydrate.
The boundary is the file, not the function
Put the directive in the leaf that needs state, effects, or a browser API. A button that tracks a copied flag does not force the whole page into the client bundle, as long as the page passes plain props into that button.
"use client";
import { useState } from "react";
export function CopyButton({ value }: { value: string }) {
const [copied, setCopied] = useState(false);
return (
<button
type="button"
onClick={async () => {
await navigator.clipboard.writeText(value);
setCopied(true);
}}
>
{copied ? "Copied" : "Copy"}
</button>
);
}Props must survive serialization
Values passed from a Server Component to a Client Component are serialized. Strings, numbers, plain objects, and arrays travel. Functions, class instances, and Date objects do not. Format the date on the server and pass a string.
- Keep data fetching in Server Components.
- Keep event handlers in Client Components.
- Pass ids and already formatted strings across the boundary.
Compose children from the server
When an interactive shell needs server-rendered content, pass that content as children. The shell can be a Client Component while the child stays a Server Component, because the child was created in a server file and handed in.
- 01
Build the interactive shell
Keep the client file small: state, events, and the markup that depends on them.
- 02
Render it from a server file
The server file fetches data and passes server content as children.
- 03
Do not refetch to avoid composition
A second fetch inside the shell usually means the boundary was drawn around too much UI.