Back to articles
ReactNext.jsServer Components

'use client' vs Server Components: When to Use Which

A clear decision rule for Server vs Client Components in the Next.js App Router — what each can do, where the boundary goes, and the mistakes that push 'use client' too high up the component tree.

Yash Thakur
4 min read
'use client' vs Server Components: When to Use Which

The short answer: default to a Server Component. Reach for "use client" only when a piece of UI genuinely needs the browser — state, effects, event handlers, or browser-only APIs — and when you do, push that directive as far down the tree as possible so you turn a leaf into a Client Component, not a whole page.

The App Router flipped React's default. Every component is a Server Component until you say otherwise, and most developers get tripped up by the same question: when do I actually need "use client"? Here's the decision rule I use, and the handful of mistakes that cause 90% of the confusion. (If the underlying model is still fuzzy, start with React Server Components, explained like you're busy and come back.)

What each kind can actually do

The split isn't about preference — each kind can do things the other can't.

Server Components run on the server (and at build time), never ship their JavaScript to the browser, and can be async. They can read the database, use secrets, and await data directly. What they can't do: hold state, run effects, attach event handlers, or touch window/localStorage.

Client Components are the React you already know. They run in the browser, so they get useState, useEffect, onClick, and every browser API — but their code is shipped to and executed by the client, and they can't be async or reach server-only resources directly.

The decision rule

Walk the questions in order and stop at the first "yes":

  1. Does it need useState, useEffect, a ref to a DOM node, or an event handler like onClick? → Client Component.
  2. Does it use a browser-only API — window, localStorage, IntersectionObserver, the clipboard? → Client Component.
  3. Does it depend on a Client-only library (many animation, chart, and form libraries call hooks internally)? → Client Component.
  4. Otherwise → leave it a Server Component. This is the default, and it's the right answer far more often than people expect.

If the answer is "yes," the follow-up matters just as much: how small can I make the client piece? A page that fetches data and renders mostly static markup should stay a Server Component, with the one interactive widget carved out as its own "use client" leaf.

The mistakes that cause the confusion

Putting "use client" at the top of the tree. The most common one. A single "use client" on a layout or page turns everything it renders into Client Components, shipping JavaScript you didn't need to ship and giving up server data fetching. The directive is contagious downward — mark the leaf, not the root.

Thinking a Client Component can't render a Server Component. It can — just not by importing it. Pass the Server Component in as children (or any prop), and it renders on the server while the client wrapper stays interactive. This is the escape hatch that keeps your client boundary small.

Trying to pass functions or class instances across the boundary. Props from a Server Component to a Client Component must be serializable — plain objects, arrays, strings, numbers. You can't hand a function or a Date-wrapped class instance down; serialize it (an ISO string, an id the client re-hydrates) instead.

Reaching for "use client" just to fetch data. You rarely need to. Fetch in the Server Component and pass the result down as a prop. Save client-side fetching for data that genuinely changes after load in response to user interaction.

The mental model that makes it click

Think of your page as mostly server-rendered HTML with small islands of interactivity. The Server Components are the page; the Client Components are the islands. Your job is to keep the islands as small as the UX allows — every component you don't mark "use client" is JavaScript the browser never has to download, parse, or run. That's the whole performance story of the App Router, and the decision rule above is just how you keep yourself on the right side of it. When in doubt, leave the directive off and see if the thing actually breaks — most of the time it doesn't. And when the boundary does bite — a client component rendering something the server didn't — that usually shows up as a hydration error, which has its own short list of causes and fixes.

Frequently asked questions

When should I use 'use client'?

Only when a component genuinely needs the browser: useState, useEffect, a DOM ref, an event handler like onClick, a browser-only API, or a client-only library. Otherwise leave it a Server Component — that's the default and usually the right answer.

Can a Client Component render a Server Component?

Yes, but not by importing it. Pass the Server Component in as children (or any prop) and it renders on the server while the client wrapper stays interactive — the escape hatch that keeps your client boundary small.

Why shouldn't I put 'use client' at the top of the tree?

The directive is contagious downward: a single 'use client' on a layout or page turns everything it renders into Client Components, shipping JavaScript you didn't need and giving up server data fetching. Mark the interactive leaf, not the root.

Written by Yash Thakur

Senior React Developer · 8+ years building for the web

More articles

Keep reading