Server Components vs Client Components in Next.js

Server Components render on the server with no client JavaScript. Client Components run in the browser and support state and events. Here is the real difference.

7 min read

Server Components vs Client Components describes the two rendering models in the Next.js App Router. A Server Component renders on the server and never ships its own JavaScript to the browser, while a Client Component renders in the browser and can hold state, respond to events, and use browser APIs. Every component in the App Router is a Server Component by default until a file opts into the client.

That opt-in happens with a directive at the top of a file: "use client". Once a file has that line, it and everything it imports and renders directly become part of the client JavaScript bundle.

Server Components vs Client Components at a glance

AspectServer ComponentClient Component
Default in the App RouterYesNo, needs the client directive
Runs whereServer onlyServer once, then the browser
Can hold state and use effectsNoYes
Can read databases, files, secrets directlyYesNo
Ships JavaScript to the browserNoYes
Can use browser globals like the window objectNoYes

The table shows the main trade-off. A Server Component is cheaper to send to the browser but cannot react to a click or hold state. A Client Component can do both of those things, at the cost of JavaScript the browser has to download, parse, and run.

What a Server Component looks like

A page or layout in the app folder is a Server Component unless you mark it otherwise. It can be an async function, and it can call a data function directly, without going through an API route.

App.tsxApp.tsx
// app/posts/[id]/page.tsx
import { getPost } from '@/lib/data'
 
export default async function PostPage({ params }: { params: Promise<{ id: string }> }) {
  const { id } = await params
  const post = await getPost(id)
  return <article><h1>{post.title}</h1><p>{post.body}</p></article>
}

This entire file runs on the server, both on the first request and again on every navigation to this route. The data function can query a database or call an internal service with a private key, because none of that code, and no key, ever reaches the browser.

What the browser receives is the rendered article HTML, not the function that produced it. This is a server-only example, since nothing in this file runs in the browser at all.

What a Client Component looks like

A Client Component needs the directive because it uses useState or responds to an event. Without it, calling a state hook in a component inside the app folder fails at build time, because Server Components cannot hold state.

App.tsxApp.tsx
// app/ui/like-button.tsx
'use client'
 
import { useState } from 'react'
 
export default function LikeButton({ likes }: { likes: number }) {
  const [count, setCount] = useState(likes)
  return <button onClick={() => setCount(count + 1)} aria-label="Like this post">{count} likes</button>
}

The directive sits on the first line because this component holds state and reacts to a click, and neither of those is available to a Server Component. Next.js still renders this component once on the server to produce the initial count in the HTML, then the browser hydrates it so the click works.

This example runs on both the server, for the first render, and the browser, for every render after that. After hydration, clicking the button updates state entirely in the browser with no round trip to the server.

Composing the two together

A Server Component can render a Client Component and pass it data through props, and a Client Component can still receive a Server Component through its children prop. This lets most of a page stay on the server while only the interactive piece runs client-side code.

Server and client boundary in a component tree

LikeButton is imported directly by the page, so it and its own imports run in the browser after the first render. A cart component passed into the modal as children is not imported by the modal file, so it still renders fully on the server, even though it visually ends up inside a Client Component. The boundary follows the import graph, not where a component sits on the page.

App.tsxApp.tsx
// app/ui/modal.tsx
'use client'
import { useState } from 'react'
 
export default function Modal({ children }: { children: React.ReactNode }) {
  const [open, setOpen] = useState(false)
  if (!open) return <button onClick={() => setOpen(true)}>Open cart</button>
  return (
    <div role="dialog">
      <button onClick={() => setOpen(false)}>Close</button>
      {children}
    </div>
  )
}

The modal file only manages open and closed state, so it is the only piece that needs the directive. A parent Server Component can render this modal with a cart component nested inside it, and the cart still fetches its own data on the server, because the modal file never imports it.

This example runs on both the server and the browser. The modal shell hydrates client-side, while the cart content it wraps was already rendered server-side before the page reached the browser. See How to Pass Props Across the Server Client Boundary for what kinds of values can cross that boundary.

Which should you use

Start every new component as a Server Component and add the client directive only when the component itself needs a client-only feature.

You needUse
Direct database or filesystem accessServer Component
State or an effect hookClient Component
A click, change, or other event handlerClient Component
A smaller JavaScript bundle for that piece of UIServer Component
A browser API like local storageClient Component

Read the exact rules for the directive itself in The use client Directive Explained, and see Composition Patterns for Server and Client Components for more layouts that mix the two.

Common mistakes

Marking a whole layout or page as a Client Component just because one small piece needs interactivity is the most common mistake. That pulls every component the file imports into the client bundle, even the parts that never change.

Move the interactive piece into its own small file instead, and keep the rest of the layout as a Server Component. Another common mistake is assuming a Server Component passed as children gets re-rendered by the Client Component wrapping it, when it does not: it rendered once on the server, and the wrapper only re-renders its own state and markup. For a closer look at exactly where the line falls, see The Server and Client Boundary in Next.js.

Rune AI

Rune AI

Key Insights

  • Every component in the App Router is a Server Component unless it or an ancestor import opts into the client.
  • Server Components can read databases and secrets directly and send no JavaScript to the browser.
  • Client Components can use state, effects, event handlers, and browser APIs.
  • A Client Component still renders once on the server first, then hydrates in the browser.
  • The use client directive marks a boundary in the module graph, not just one component.
  • A Server Component passed as children to a Client Component still renders on the server.
  • Default to Server Components and add the directive only where interactivity is needed.
RunePowered by Rune AI

Frequently Asked Questions

Are all components Server Components by default in the App Router?

Yes. Every component inside the app folder is a Server Component unless the file, or a file it imports from, has the use client directive at the top.

Do Client Components ever run on the server?

Yes. Next.js renders a Client Component once on the server to produce the initial HTML, then the browser hydrates that HTML so the component becomes interactive. This does not apply to a component dynamically imported with server rendering disabled.

Can a Server Component use useState or useEffect?

No. Those hooks only work in Client Components. A Server Component that calls them fails to build.

Does marking a component as a Client Component also convert every component inside it?

It converts every component that file imports and renders directly. A Server Component passed to it as children or another prop is not converted, because it is not part of that file's module graph.

Conclusion

Server Components and Client Components solve different problems in the Next.js App Router. A Server Component keeps data fetching and secrets on the server and ships no JavaScript for that piece of UI, while a Client Component adds interactivity at the cost of a JavaScript bundle. Default to Server Components and opt in to a Client Component only where a piece of UI actually needs state, effects, or browser APIs.A Server Component runs only on the server and can reach backend resources directly, while a Client Component runs in the browser after an initial server render and can hold state, run effects, and respond to events. Keep components as Server Components by default, and add the client directive only to the specific files that need interactivity, so the rest of the page stays out of the JavaScript bundle sent to the browser. This split is not about which model is better. It is about matching each piece of UI to the environment it actually needs. A product page mostly reads data and renders text, so it belongs on the server, while a cart icon that tracks an open count belongs on the client. Start a new component as a Server Component, write it, and only reach for the client directive when the build fails because a hook or an event handler needs it. That habit alone keeps most of an application's JavaScript bundle small without any extra planning.