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
| Aspect | Server Component | Client Component |
|---|---|---|
| Default in the App Router | Yes | No, needs the client directive |
| Runs where | Server only | Server once, then the browser |
| Can hold state and use effects | No | Yes |
| Can read databases, files, secrets directly | Yes | No |
| Ships JavaScript to the browser | No | Yes |
| Can use browser globals like the window object | No | Yes |
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/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/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.
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/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 need | Use |
|---|---|
| Direct database or filesystem access | Server Component |
| State or an effect hook | Client Component |
| A click, change, or other event handler | Client Component |
| A smaller JavaScript bundle for that piece of UI | Server Component |
| A browser API like local storage | Client 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
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.
Frequently Asked Questions
Are all components Server Components by default in the App Router?
Do Client Components ever run on the server?
Can a Server Component use useState or useEffect?
Does marking a component as a Client Component also convert every component inside it?
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.
More in this topic
`generateMetadata` Explained with Real Examples
What generateMetadata does, when it runs, and how to use it for real routes: awaited params, deduplicated data fetching, extending parent metadata, and returning a 404 from metadata.
Canonical URLs in Next.js: `metadataBase`, `alternates.canonical`, and Dynamic Pages
How canonical URLs work in the Next.js App Router: setting metadataBase once, writing alternates.canonical per route, handling dynamic segments, and what happens when the base URL is missing.
Open Graph and Twitter Card Metadata in Next.js
How to write Open Graph and Twitter card metadata in the Next.js App Router: the openGraph and twitter fields, automatic card defaults, article tags, and image merge rules.