When to use each
| Need | Use |
|---|---|
State and event handlers (onClick, onChange) | Client Component |
Lifecycle logic (useEffect) | Client Component |
Browser-only APIs (localStorage, window, Navigator.geolocation) | Client Component |
| Custom hooks that use the above | Client Component |
| Fetch data from databases or APIs | Server Component |
| Access API keys or secrets without exposing them | Server Component |
| Reduce JavaScript sent to the browser | Server Component |
| Improve First Contentful Paint (FCP) | Server Component |
<Page> Server Component can fetch post data and pass it as props to a <LikeButton> Client Component that handles interactivity:
How they work in Next.js
On the server
Next.js uses React’s APIs to split rendering work by individual route segments (layouts and pages):- Server Components are rendered into a compact binary format called the React Server Component Payload (RSC Payload).
- Client Components and the RSC Payload are used to prerender HTML.
- The rendered output of Server Components.
- Placeholders for where Client Components should be rendered, along with references to their JavaScript files.
- Any props passed from a Server Component to a Client Component.
On the client (first load)
- HTML is used to immediately show a fast, non-interactive preview.
- RSC Payload reconciles the Client and Server Component trees.
- JavaScript hydrates Client Components and makes the app interactive.
Subsequent navigations
- The RSC Payload is prefetched and cached for instant navigation.
- Client Components are rendered entirely on the client, without server-rendered HTML.
Using Client Components
Add the'use client' directive at the top of the file, above imports, to mark a component as a Client Component:
'use client' declares a boundary between the Server and Client module graphs. Once a file is marked with 'use client', all its imports and child components are considered part of the client bundle — you do not need to add the directive to every client component individually.
Reducing JS bundle size
Add'use client' only to specific interactive components rather than marking large parts of your UI as Client Components.
For example, a <Layout> contains mostly static elements but includes an interactive search bar. Only <Search /> needs to be a Client Component:
Passing data from Server to Client Components
Pass data from Server Components to Client Components using props:Props passed to Client Components must be serializable by React. Non-serializable values (functions, class instances, etc.) cannot be passed directly.
Interleaving Server and Client Components
You can pass Server Components as props to a Client Component, allowing you to visually nest server-rendered UI inside Client Components. A common pattern is to usechildren to create a slot in a Client Component. For example, a <Cart> that fetches data on the server inside a <Modal> that uses client state to toggle visibility:
<Cart> as a child of <Modal>:
Context providers
React context is not supported in Server Components. To share global state (like theme), create a Client Component that wrapschildren:
app/layout.tsx):
Third-party components
When using a third-party component that relies on client-only features (e.g.useState) but does not include a 'use client' directive, wrap it in your own Client Component:
<Carousel /> directly inside a Server Component:
Preventing environment poisoning
JavaScript modules can be shared between Server and Client Components, making it possible to accidentally import server-only code into the client. For example:lib/data.ts
API_KEY that should never reach the client. In Next.js, only environment variables prefixed with NEXT_PUBLIC_ are included in the client bundle — non-prefixed variables are replaced with an empty string.
To prevent accidental usage in Client Components, use the server-only package:
lib/data.js
client-only package can mark modules that contain client-only logic (e.g. code that accesses window).
- npm
- yarn
- pnpm
- bun
Installing
server-only or client-only is optional in Next.js. However, they are useful if your linting rules flag extraneous dependencies, and they provide clearer build-time error messages when a module is used in the wrong environment.