The "use client" Boundary
"use client" marks a boundary: the component and everything it imports become client code.
The "use client" directive does not just affect one component — it creates a boundary. Once a file is marked client, every component it imports is pulled into the client bundle too. So you push "use client" as far down the tree as possible, keeping most of your app as Server Components.
A useful pattern
Server Components can import and render Client Components, and can pass them serializable props. But a Client Component cannot import a Server Component directly — instead, pass Server-rendered UI to it via children.
Example
// A Server Component composing a small Client island
import Counter from './counter'; // 'use client' lives inside counter.tsx
export default function Page() {
return (
<section>
<h1>Server-rendered heading</h1>
<Counter /> {/* interactive client island */}
</section>
);
}When to use it
- A developer places 'use client' at the top of a modal component so its open/close state and event handlers work in the browser.
- A team keeps server data-fetching in a parent Server Component and passes data as props to a 'use client' child for interactivity.
- A charting library that relies on the DOM is wrapped in a 'use client' component so it never attempts to run on the server.
More examples
Boundary at component file top
'use client' must appear before any imports; the directive turns this file into the client bundle boundary.
'use client';
// Everything below this line — and all imports from this file —
// will be included in the client JavaScript bundle.
import { useState } from 'react';
export default function Toggle() {
const [on, setOn] = useState(false);
return <button onClick={() => setOn(v => !v)}>{on ? 'ON' : 'OFF'}</button>;
}Server parent, client child
Server Components can import and render Client Components, passing serialisable props across the boundary.
// app/page.tsx — Server Component
import LikeButton from './LikeButton'; // client
export default async function Page() {
const data = await fetch('/api/post').then(r => r.json());
return (
<article>
<h1>{data.title}</h1>
<LikeButton initialCount={data.likes} />
</article>
);
}Dynamic import to isolate boundary
ssr: false prevents a DOM-dependent client component from running during server-side rendering.
import dynamic from 'next/dynamic';
const Chart = dynamic(() => import('./Chart'), { ssr: false });
export default function DashboardPage() {
return <Chart data={[1, 2, 3]} />;
}
Discussion