You now know how to create pages, layouts, and navigation in the Next.js App Router. The next major concept is understanding where component code belongs. Some components can stay on the server; others need browser-side state, events, or APIs.
This guide builds a practical server-first mental model, explains 'use client', and shows how to compose both component types without sending unnecessary code or private data to the browser.
Server vs Client Components at a Glance
Start with the capability a component needs. Server Components are suited to server data access, secrets, data transformation, and UI that does not require browser interaction. Client Components are suited to state, event handlers, effects, custom hooks, and browser APIs.
- Database and APIs
- Secrets and private logic
- Data transformation
- Rendered component output
- State and events
- Effects and custom hooks
- Browser APIs
- Interactive UI
This is a composition model, not a choice between two separate applications. On an initial request, Next.js can use Server and Client Components to pre-render HTML. In the browser, JavaScript hydrates the Client Components so their event handlers work. Later navigations use the RSC payload to update the tree.
What Is a Server Component?
A Server Component is rendered in the server environment and is the default in the App Router. It does not need a directive. A simple page with static content is already a Server Component:
export default function AboutPage() {
return (
<main>
<h1>About</h1>
<p>This component does not need browser interactivity.</p>
</main>
)
}Server Components can call server-side data functions close to the data source. They can also keep API credentials and private implementation details out of the client bundle. That does not make every server value safe to expose: explicitly select the fields the interface needs. The next tutorial explains Next.js data fetching with APIs, databases, and streaming in detail.
type Post = { id: string; title: string }
async function getPosts(): Promise<Post[]> {
// Tutorial data. Replace this with your real server data source.
return [
{ id: '1', title: 'Server Components' },
{ id: '2', title: 'Client Components' },
]
}
export default async function BlogPage() {
const posts = await getPosts()
return (
<main>
<h1>Blog</h1>
{posts.map((post) => (
<article key={post.id}><h2>{post.title}</h2></article>
))}
</main>
)
}What Is a Client Component?
A Client Component is part of the client module graph and can use React state, events, effects, and browser APIs. Add 'use client' at the top of its entry file, before imports:
'use client'
import { useState } from 'react'
export function Counter() {
const [count, setCount] = useState(0)
return (
<button type="button" onClick={() => setCount(count + 1)}>
Count: {count}
</button>
)
}The browser is required because a user click triggers onClick, which updates retained state through setCount and causes React to update the button.
- User clicks
- Browser event
onClicksetCount()- UI updates
Server Components Are the Default
Files such as app/page.tsx, app/layout.tsx, and app/blog/page.tsx are Server Components unless a client boundary changes the relevant module graph. This matches the route model explained in our layouts and pages guide.
app/
|-- layout.tsx # Server Component
|-- page.tsx # Server Component
`-- blog/
`-- page.tsx # Server ComponentThere is no 'use server' directive for declaring a component as a Server Component. That directive has a different purpose: it marks Server Functions callable across the network boundary.
Understanding 'use client'
The directive defines an entry point into the client module graph. Once a module is marked, its imports and transitive dependencies are treated as client code when reached through that boundary. You do not need to repeat the directive in every imported interactive child.

'use client'When to Use Each Component Type
| Requirement | Prefer | Why |
|---|---|---|
| Read from a database or private API | Server Component | Keeps access and secrets on the server |
| Render static article or product details | Server Component | No browser state is required |
useState, useEffect, custom hooks | Client Component | Requires a client lifecycle |
onClick or onChange | Client Component | Browser event handlers provide interaction |
window, document, localStorage | Client Component | Those APIs exist in the browser |
Navigation hooks such as useRouter | Client Component | Hooks react to browser-side navigation state |
Client Components are not a failure or a fallback. They are the correct place for interaction. The goal is to choose a boundary that includes the interaction without pulling unrelated server-capable content into the client bundle.
State, Events, and Browser APIs
A Server Component cannot retain browser state or register an onClick handler. It also cannot read window, document, or localStorage because those objects do not exist in the server environment. Move only the part that needs those capabilities into a Client Component.
Navigation hooks follow the same rule. The useRouter example from Blog #4 is a Client Component because it responds to a click. A normal Link does not require your surrounding file to become a Client Component.
Keep Data and Secrets on the Server
Server Components can call private services without bundling credentials into browser JavaScript. Keep environment variables, database clients, privileged API tokens, and authorization logic in server-only modules. Prefixing an environment variable with NEXT_PUBLIC_ intentionally makes it available to client code, so never use that prefix for a secret.
- SecretServer environment
- Private requestAuthorized data source
- Filter resultSelect safe fields
- Serializable propsMinimal public data
- Client UIBrowser interaction
Add import 'server-only' to modules that must never enter the client graph. Next.js then reports a build-time error if a Client Component imports them. This complements—not replaces—careful data selection and authorization.
Pass Server Data to a Client Component
A Server Component can fetch or calculate data and pass the small interactive component only what it needs:
import { LikeButton } from './like-button'
export default async function ProductPage() {
const product = await getProduct()
return (
<main>
<h1>{product.name}</h1>
<LikeButton productId={product.id} />
</main>
)
}'use client'
export function LikeButton({ productId }: { productId: string }) {
return <button type="button">Like product {productId}</button>
}Props crossing from server to client must be serializable by React. Strings, numbers, booleans, arrays, and plain data objects are common safe choices. Do not pass a database connection, class instance, arbitrary function, request object, or secret-bearing record. Server Functions are a special supported function type, but they still require validation and authorization when called.
Can Server and Client Components Be Mixed?
Yes. A Server Component can import and render a Client Component normally. A product page might keep its heading, details, description, and reviews on the server while a small add-to-cart control handles browser interaction.
Keep the interactive boundary small. Only the controls that retain state or handle events need browser JavaScript.
The reverse direction needs composition rather than a direct import. A Client Component should not import an arbitrary Server Component and expect it to stay server-only. Instead, a parent Server Component renders the server content and passes that content into a Client Component through children or another slot:
// app/ui/modal.tsx
'use client'
export function Modal({ children }: { children: React.ReactNode }) {
return <div className="modal">{children}</div>
}
// app/page.tsx — Server Component
import { Modal } from './ui/modal'
import { Cart } from './ui/cart'
export default function Page() {
return <Modal><Cart /></Modal>
}Cart is rendered on the server ahead of time, while Modal controls client-side visibility. The slot lets server-rendered UI appear visually inside interactive client UI without importing the server module into the client graph.
Why the Client Boundary Matters
Client modules contribute JavaScript that the browser downloads and evaluates. Their interactive output must also hydrate so event handlers attach. A boundary that wraps a large mostly static page can increase bundle size, hydration work, and mental overhead.
'use client'
export default function EntireArticlePage() {
// A large mostly static article does not need to be client code.
}A better component tree keeps ArticlePage and ArticleContent on the server, then renders a focused CopyButton Client Component. Do not invent a performance percentage; inspect the actual bundle and user experience.
Context Providers and Third-Party Components
React context is not supported directly in Server Components, so place the provider implementation in a Client Component and render it from a Server Component. Wrap only the subtree that needs the value. Keeping the provider as deep as practical gives Next.js more static server structure to optimize.
'use client'
import { createContext } from 'react'
export const ThemeContext = createContext('light')
export function ThemeProvider({ children }: { children: React.ReactNode }) {
return <ThemeContext.Provider value="light">{children}</ThemeContext.Provider>
}Third-party components that use state, effects, or browser APIs also require a client boundary. A library that already declares its client entry can be imported directly. If it does not, create a small wrapper marked 'use client'. Do not assume every package needs this treatment.
Build a Server-First Product Card

import { FavoriteButton } from './favorite-button'
const tutorialProduct = {
id: 'headphones-1',
name: 'Studio Headphones',
price: '$129',
}
export function ProductCard() {
return (
<article>
<div aria-hidden="true">Product image placeholder</div>
<h2>{tutorialProduct.name}</h2>
<p>{tutorialProduct.price}</p>
<FavoriteButton productId={tutorialProduct.id} />
</article>
)
}'use client'
import { useState } from 'react'
export function FavoriteButton({ productId }: { productId: string }) {
const [saved, setSaved] = useState(false)
return (
<button
type="button"
aria-pressed={saved}
onClick={() => setSaved(!saved)}
>
{saved ? 'Saved' : `Save ${productId}`}
</button>
)
}The mock object is clearly tutorial data. The card structure and text need no state, so they remain on the server. Only the favorite button enters the client graph.
Common Server and Client Component Mistakes
- Adding
'use client'everywhere. This pulls imported modules into the client graph and weakens the server-first boundary. - Calling
useStatein a Server Component. Move the state and controls into a Client Component. - Reading browser APIs during server rendering. Access
window,document, andlocalStoragefrom appropriate client logic. - Exposing secrets through props. Filter data and pass only safe fields needed by the interface.
- Making an entire layout client-side for one control. Keep the layout on the server and import a small interactive child.
- Importing server-only code into client code. Use clear module boundaries and
server-onlyguards. - Thinking the directive affects only one component function. It defines a module boundary that includes its imported dependency graph.
- Thinking Client Components only participate in browser rendering. Next.js can pre-render them into initial HTML on the server, then hydrate them in the browser.
Server and Client Component Best Practices
- Start with Server Components and add a client boundary where interaction requires it.
- Keep secrets, database clients, and privileged APIs server-side.
- Pass minimal serializable data into Client Components.
- Keep interactive modules focused and avoid converting whole pages or layouts.
- Use server composition and
childrenwhen server content must appear inside client UI. - Place context providers as deep as the consuming subtree allows.
- Check whether third-party packages already expose an appropriate client entry.
- Keep component files discoverable using the conventions from the Next.js project structure guide.
- Verify framework behavior against current official documentation as React and Next.js evolve.
Frequently Asked Questions
Are Next.js components Server Components by default?
Yes. In the App Router, layouts and pages are Server Components by default unless a client boundary brings a module into the client graph.
What does use client mean in Next.js?
The use client directive marks a module as a client entry point. Its imports and transitive dependencies become part of the client module graph.
When should I use a Client Component?
Use one for state, event handlers, effects, custom hooks, or browser APIs such as localStorage and window.
Can I use useState in a Server Component?
No. Server Components do not retain browser state or attach event handlers. Move stateful UI into a focused Client Component.
Can a Server Component import a Client Component?
Yes. A Server Component can render an imported Client Component and pass it serializable props.
Can a Client Component import a Server Component?
Do not import a Server Component into the client module graph. Instead, a Server Component can pass already server-rendered content to a Client Component through a prop such as children.
Can Server Components access a database?
They can call server-side data-access code, subject to your runtime and security architecture. Return only the fields the rendered interface needs.
Are Client Components bad for performance?
No. They are the correct tool for interactivity. Performance problems arise when client boundaries include much more JavaScript and hydration work than the interface requires.
Official Resources
Continue with the official Next.js Server and Client Components documentation and the use client reference. React's client directive reference provides the detailed serialization model.
Next Steps
You can now keep pages server-first, add focused interaction, pass safe serializable props, and compose server-rendered content through client wrappers. Continue by learning to fetch data in the right part of the component tree.
