Next.jsReactServer ComponentsApp Router

Next.js Server vs Client Components: How to Choose

Choose between Server Components and Client Components in the Next.js App Router by looking at data access, state, events, browser APIs, serialization, and bundle size.

·Updated ·10 min read·Counting...
Next.js Server vs Client Components: How to Choose

In the Next.js App Router, pages and layouts are Server Components by default. Add "use client" only when a component needs state, event handlers, effects, browser APIs, or client-side context. The goal is not to make everything server-only. The goal is to keep the client boundary as small and intentional as possible.

Quick decision table

Need Server Component Client Component
Read a database or server secret directly Yes No
Fetch public content on the server Yes Sometimes, but often unnecessary
Use useState or useReducer No Yes
Use onClick or onChange No Yes
Use useEffect or window No Yes
Reduce JavaScript sent to the browser Yes No

Start server-side, then move only interactive leaf components to the client.

Default Server Components

// app/blog/page.tsx
export default async function BlogPage() {
  const posts = await getPosts()

  return (
    <main>
      <h1>Blog</h1>
      <PostList posts={posts} />
    </main>
  )
}

A Server Component can read server-side data without shipping its implementation as interactive JavaScript. It cannot use event handlers, state, effects, or browser globals.

Keep "use client" narrow

If only a search box is interactive, mark only the search box:

// components/SearchBox.tsx
"use client"

import { useState } from "react"

export function SearchBox() {
  const [query, setQuery] = useState("")
  return <input value={query} onChange={(event) => setQuery(event.target.value)} />
}

The page can remain a Server Component:

import { SearchBox } from "@/components/SearchBox"

export default async function BlogPage() {
  const posts = await getPosts()
  return (
    <>
      <SearchBox />
      <PostList posts={posts} />
    </>
  )
}

"use client" defines a module graph boundary. The file and the dependencies it imports become part of the client module graph. Avoid placing it at the top of a large page or root layout just to support one button.

Server UI can be passed into Client UI

Client Components can receive already-rendered server UI through children:

// components/Modal.tsx
"use client"

export function Modal({ children }: { children: React.ReactNode }) {
  return <dialog>{children}</dialog>
}
<Modal>
  <ServerRenderedArticle />
</Modal>

Do not import server-only modules directly into a Client Component file. Compose them from a Server Component parent instead.

Props must be serializable

Data crossing from a Server Component to a Client Component must be serializable. Plain objects, arrays, strings, numbers, and booleans are usually fine. Database connections, class instances, arbitrary functions, and complex runtime objects are not.

Dates should be converted deliberately, usually to strings, then formatted with clear timezone behavior. Otherwise the server and browser may render different initial output.

What happens on first load

Next.js renders Server Components on the server and produces the React Server Component payload. The browser receives HTML for the initial view, then hydrates Client Components with JavaScript so they become interactive.

This means Client Components may still be pre-rendered into initial HTML. "Client Component" does not mean "only rendered in the browser." If the server render and the first browser render differ, you can still get a hydration mismatch. For debugging, see React Hydration Failed: Causes, Debugging, and Fixes.

Common mistakes

  • Adding "use client" to an entire page for a single interactive control.
  • Reading window in a Server Component.
  • Passing private environment variables into browser code.
  • Passing non-serializable objects across the server-client boundary.
  • Refetching first-screen data in useEffect when the server could fetch it directly.
  • Using typeof window to render different initial JSX.
Server Page
├── Server Header
├── Server ArticleList
│   └── Client FavoriteButton
└── Client SearchPanel
    └── Initial data from the server

Move client boundaries up only when multiple interactive components truly need shared client state.

Subscribe to FreeMac

Weekly picks: free Mac software reviews, trusted source updates, alternatives, and low-friction guides.