
Server Components vs Client Components in Next.js: A Decision Framework
The use client directive gets sprinkled on components defensively far more often than necessary. A simple decision framework removes most of the guesswork and keeps the client bundle smaller as a side effect.
TL;DR / Written for skimmers
The App Router default is a server component, and that default is usually correct. Data fetching, static markup and anything that does not depend on the browser belongs there, rendered once on the server. A component needs use client when it uses state or effects, when it relies on browser-only APIs like localStorage or window, or when it needs interactive event handlers like onClick that require hydration. The smaller the client component, the smaller the JavaScript shipped to the browser. A page with one interactive button does not need the entire layout marked as a client component, just that button. If the data can be fetched on the server before render, fetching it in a server component avoids the loading state entirely and removes a whole class of effect-based data fetching bugs.
Start every component as a server component
The App Router default is a server component, and that default is usually correct. Data fetching, static markup and anything that does not depend on the browser belongs there, rendered once on the server.
Starting from this default, instead of reaching for use client out of habit, means the directive only shows up where it earns its place.
The three real reasons to add use client
A component needs use client when it uses state or effects, when it relies on browser-only APIs like localStorage or window, or when it needs interactive event handlers like onClick that require hydration.
Outside of those three reasons, a component can almost always stay on the server. Reaching for use client because a file imports another client component is a sign to look at the import, not to relabel everything upstream.
Pushing client boundaries down, not up
The smaller the client component, the smaller the JavaScript shipped to the browser. A page with one interactive button does not need the entire layout marked as a client component, just that button.
Passing server-rendered content as children into a small client wrapper keeps the boundary tight and avoids accidentally turning a whole page into client-rendered markup.
A quick checklist before you reach for useEffect
If the data can be fetched on the server before render, fetching it in a server component avoids the loading state entirely and removes a whole class of effect-based data fetching bugs.
useEffect is for synchronizing with something outside React, not for fetching data that the server could have handed over already rendered. Reaching for it first is usually the more expensive path, not the simpler one.