Loading lessons...
Interleaving Server and Client Components
Interleaving Server and Client Components
To build performant Next.js applications, you must know how to compose Server Components and Client Components together without breaking the RSC boundary.
Rule #1: You CANNOT import a Server Component into a Client Component
If a file marked with 'use client' directly imports a Server Component file, that component will be forcibly converted into a Client Component!
// ❌ WRONG PATTERN
'use client';
// HeavyServerList will be bundled for the client!
import HeavyServerList from './HeavyServerList';
export default function ClientContainer() {
const [isOpen, setIsOpen] = useState(false);
return (
<div>
<button onClick={() => setIsOpen(!isOpen)}>Toggle</button>
{isOpen && <HeavyServerList />}
</div>
);
}
Rule #2: Pass Server Components as children or Props
To render a Server Component inside a Client Component, pass the Server Component as a React node prop (such as children) from an overarching Server Component!
// ✅ CORRECT PATTERN - Client Component acts as a wrapper slot
// components/ClientModal.tsx
'use client';
import { useState } from 'react';
export default function ClientModal({ children }: { children: React.ReactNode }) {
const [open, setOpen] = useState(true);
return (
<div className="relative">
<button onClick={() => setOpen(!open)}>Toggle Modal</button>
{open && <div className="p-4 border shadow-lg">{children}</div>}
</div>
);
}
// app/page.tsx - Server Component composition root
import ClientModal from '@/components/ClientModal';
import HeavyServerList from '@/components/HeavyServerList';
export default async function Page() {
return (
<main>
<h1>Dashboard Page</h1>
{/* HeavyServerList remains a pure Server Component! */}
<ClientModal>
<HeavyServerList />
</ClientModal>
</main>
);
}
Serialization Boundary Rules
When passing props from a Server Component to a Client Component, the prop values must be serializable across the network boundary.
| Allowed Prop Types (Serializable) | Disallowed Prop Types |
|---|---|
| Strings, Numbers, Booleans | Functions (onClick={() => ...}) |
| Objects & Arrays (plain) | Class Instances |
null & undefined | Symbols & Unserializable closures |
JSX Elements (via children slot) | Complex Streams / File Handles |
TL;DR
- Never import a Server Component directly inside a
'use client'module. - Pass Server Components as
childrenor JSX props to Client Components. - Data passed as props across the Server-to-Client boundary must be serializable JSON-like data.
- Keep Client Components at the visual leaves of your component tree.