# Server vs Client Components in Next.js: A Beginner Guide

> The difference between server and client components in Next.js for beginners: use client, props, the children pattern, a decision table and common errors.

URL: https://www.matthewswong.com/en/blog/perbedaan-server-component-client-component-nextjs/

- Author: Matthews Wong (https://www.matthewswong.com/en/about/)
- Published: 2026-10-17
- Updated: 2026-10-17
- Category: Web Dev

> **Key takeaway:** The difference between server and client components in Next.js: in the App Router every component is a Server Component by default, rendered on the server and shipping no JavaScript of its own. Add use client on the first line of a file only for parts that need state, event handlers or browser APIs, and pass serializable props.

You write useState in a Next.js page, the build fails, and the error tells you to add use client. You do, and the error goes away. A few minutes later you pass an onClick function as a prop from the page to a button component, and a new error appears about props that cannot be serialized. This is where beginners usually get stuck: they know there are two kinds of component but not the rule behind them.

This guide walks you through six small steps in a fresh app, from the default component to the children pattern, hydration and server-only code. Every rule is checked against the official Next.js and React docs. The example in step 5 uses the same pattern as this site's SiteChrome component, and the other facts match the free lesson linked above.

Want to see this site's real code, including the 3D game route that loads its engine only after the visitor agrees? Take the free lesson

Learn Server and Client Components in Next.js from Real Code

 in the Web app skills track, with an interactive explainer and a quiz.

## What you need

No large project is required. One empty app is enough to try every step.

A supported Node.js and a terminal. The commands below use npm.

A fresh Next.js app with the App Router, created with create-next-app in step 1. Accept the defaults.

A code editor and a browser. You will read the raw HTML from the server with curl instead of only looking at the inspector.

## Step 1: Know the default, every component is a Server Component

Create the app and start it. In the app folder, files such as layout.tsx and page.tsx are Server Components because nothing above them says otherwise. The Next.js docs say layouts and pages are Server Components by default. A Server Component renders on the server and the browser receives only the rendered output, so that code can query a database or use an API key without leaking it.

There is one price: a Server Component has no state and no event handlers. Server code runs once and its output is sent, and no component stays alive in the browser to hold state. Almost every beginner error comes from this rule, so learn it before moving on.

## Step 2: Create your first Client Component with use client

Now create one small file that genuinely needs state: a like button. Add use client on the very first line, before any import. The React docs say the directive must be at the very beginning of a file, above any imports or other code (comments are fine), written with single or double quotes but not backticks. A directive placed below the imports is not recognised.

Note that use client applies to the whole module and cannot go inside a function or component. You only need it on files whose components a Server Component renders directly. Other files that file imports join the browser bundle without needing a directive of their own.

## Step 3: Use it from a server page and pass safe props

The page stays a Server Component. It imports the button and passes a plain number as a prop. Then run curl to see the HTML the server sent. The page heading is already inside, even though the button is a Client Component. That is because on the first load Client Components are also prerendered to HTML on the server, then JavaScript hydrates them to attach event handlers. A Client Component does not mean browser-only.

Props that cross the use client boundary must be serializable so React can send them from server to browser. According to the React docs that includes strings, numbers, booleans, null, Date, arrays, Map, Set, plain objects, JSX elements and Promises. Ordinary functions and class instances are not allowed. That is why this site's PlayShell takes a locale string and a number, not a callback such as onClose.

> Do not fix a props error by putting use client on the page. That only moves the whole page and all of its imports into the browser bundle. Move the handler inside the Client Component and send only plain data from the server.

## Step 4: Put the use client boundary as deep as you can

The use client directive pulls everything that file imports into the client bundle, so the higher you put it, the more JavaScript you ship. The Next.js docs give an example: a layout with a static logo and navigation links, plus one interactive search bar. Only the search bar needs to be a Client Component. The logo and the layout stay on the server.

A good habit: start with Server Components for everything, then move interactive pieces into their own small files one at a time. Do not reverse the order by marking the page as a client and trying to repair it later.

## Step 5: Mix both with the children pattern

Sometimes an interactive part needs to wrap content rendered on the server, such as a modal that opens and closes with state but whose content is a cart loaded from a database. Pass the Server Component as children to the Client Component. The Client Component only provides a slot. According to the Next.js docs, a component passed as children or another prop is not imported into the client module graph, is rendered on the server ahead of time, and reaches the Client Component as rendered output.

This site's SiteChrome component uses the same pattern. It must read the pathname, so it is a Client Component, but the header and footer are rendered in the server layout and passed in as ReactNode props. SiteChrome only decides whether to show them.

The same applies to providers such as a theme. React context is not supported in Server Components, so create a small provider with use client that accepts children, then mount it in the server layout. The docs advise rendering providers as deep as possible: wrap children, not the whole html element.

## Step 6: Avoid hydration errors and leaking server code

Because a Client Component renders twice, once on the server and once in the browser during hydration, both results must match. The Next.js docs list the common causes: using Date, window or localStorage directly in render logic, checking typeof window, and wrongly nested HTML such as a p inside a p. The fix is to render the same thing on both sides, then fill in browser-only values in useEffect.

For a full worked example and how to read the error, see

. The docs give two other ways out: load the component with next/dynamic and ssr false inside a Client Component, or use suppressHydrationWarning on a single element such as a timestamp. The second is an escape hatch, not something to overuse.

## When to use which: a decision table

Ask one question about each component: does it need something that exists only in the browser? If not, leave it as a Server Component. The list below follows the guidance in the Next.js docs.

| You need | Choose | Short reason |
| --- | --- | --- |
| useState, onClick, onChange | Client Component | A Server Component cannot hold state or receive events. |
| useEffect or a custom hook | Client Component | Lifecycle logic runs in the browser. |
| window, localStorage, navigator.geolocation | Client Component | Browser-only APIs do not exist on the server. |
| Fetching data from a database or API | Server Component | Close to the data source, and the result adds no JavaScript. |
| API keys, tokens and other secrets | Server Component | They are never sent to the browser. |
| Article text and static content | Server Component | Less JavaScript, and the text is in the first HTML. |

## Common errors and how to fix them

These six symptoms stop beginners most often. Match the message you see against the first column.

| Symptom | Cause | Fix |
| --- | --- | --- |
| An error saying a React hook only works in a Client Component | A file uses useState or useEffect but has no use client, so it counts as a Server Component. | Move the interactive part into a small file with use client on the first line. The page stays on the server. |
| Props reported as not serializable | A function was passed from a Server Component to a Client Component. | Pass plain data and define the handler inside the Client Component. |
| Hydration error: Text content does not match server-rendered HTML | The server output differs from the first browser render, for example because of Date, window or wrongly nested HTML. | Render the same value on both sides, then fill in browser-only values in useEffect. |
| An error that ssr false is not allowed with next/dynamic in a Server Component | The ssr false option is only supported in Client Components. | Move the dynamic import into a file that starts with use client. |
| A data call fails quietly in the browser | A helper that reads a secret was imported into a Client Component. Variables without the NEXT_PUBLIC_ prefix become an empty string in the browser. | Add an import of server-only at the top of the helper so you get a build error instead. |
| A third-party library component errors in a Server Component | The library uses state but its module has no use client. | Wrap it in a small file of your own that starts with use client, then import the wrapper. |

## Checklist before you move on

Pages and layouts stay Server Components, with no use client above them.

use client is on the first line, only in small files that really need state, effects or browser APIs.

Props that cross the boundary hold plain data, with no functions.

Server content that an interactive component must wrap is passed through children.

Code that reads secrets imports server-only, and browser values such as Date or window are moved into useEffect.

## Learn more

This guide focuses on practical steps. For the full picture, including how this site's game route holds back the 3D engine until the visitor agrees and why page text must never sit behind a loading skeleton, open the lesson

. When you start using forms and server-side mutations, also read

, because the server and client boundary decides what is safe there too.

A rule you can carry: start from Server Components, add use client only to the smallest piece that needs the browser, and send across that boundary only data that can be serialized.

## Sources and official reading

- Next.js docs: Server and Client Components
- Next.js docs: the use client directive
- React docs: use client
- Next.js docs: lazy loading Client Components
- Next.js docs: Text content does not match server-rendered HTML error

## Source links

- https://nextjs.org/docs/app/getting-started/server-and-client-components
- https://nextjs.org/docs/app/api-reference/directives/use-client
- https://react.dev/reference/rsc/use-client
- https://nextjs.org/docs/app/guides/lazy-loading
- https://nextjs.org/docs/messages/react-hydration-error

## FAQ

### What is the difference between a server component and a client component in Next.js?

A Server Component renders on the server and sends only its output to the browser, so it ships no JavaScript of its own. A Client Component is marked with use client and can use state, effects and browser APIs. In the App Router, layouts and pages are Server Components by default.

### Why does Next.js say I need use client when I use useState?

Server Components are never sent to the browser, so they cannot hold state or run effects. Hooks such as useState and useEffect only work in Client Components. Move the interactive part into a small file that starts with use client and keep the page on the server.

### Does use client mean the component only runs in the browser?

No. On the first load, Client Components are also prerendered to HTML on the server, then hydrated in the browser so event handlers attach. Use client changes what is shipped as JavaScript, not whether the first HTML exists.

### Can I pass a function from a server component to a client component?

No, not an ordinary function. Props that cross the use client boundary must be serializable, and a plain function is not. Define the handler inside the Client Component and pass only data such as strings, numbers or plain objects.

### Why do I get a hydration error in Next.js?

The HTML the server rendered differs from what the browser rendered first. Common causes are using Date, window or localStorage in render logic and wrongly nested HTML like a p inside a p. Render the same value on both sides, then set browser-only values in useEffect.

---

- HTML version: https://www.matthewswong.com/en/blog/perbedaan-server-component-client-component-nextjs/
- Bahasa Indonesia: https://www.matthewswong.com/id/blog/perbedaan-server-component-client-component-nextjs/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
