Client Components
Overview
If Server Components are the new default, how do we build interactive UI elements like dropdowns, forms, or buttons?
We must explicitly declare Client Components. A Client Component is exactly what traditional React was for its entire history: a component whose JavaScript is bundled, shipped to the browser, and executed on the client-side, allowing it to maintain state and listen to DOM events.
In the Next.js App Router, you declare a Client Component by placing the directive `"use client";` at the absolute top of your file. This tells the bundler: 'Hey, this file (and everything it imports) contains interactivity. Please package this JavaScript and send it to the user's browser.'
Syntax
// This directive MUST be the very first line of code (before imports!)
"use client";
import { useState } from 'react';
export default function ToggleThemeButton() {
// We can safely use State because we declared 'use client'
const [isDark, setIsDark] = useState(false);
return (
<button
onClick={() => setIsDark(!isDark)}
className={isDark ? 'bg-black text-white' : 'bg-white text-black'}
>
Switch to {isDark ? 'Light' : 'Dark'} Mode
</button>
);
}Common Pitfalls
- The 'use client' Contagion Effect: If a component has
"use client"at the top, it becomes a Client Component. Crucially, EVERY component that it imports also becomes a Client Component, whose JS gets shipped to the browser. If you put"use client"at the very top of yourlayout.js, you have accidentally turned your entire application back into a legacy SPA, destroying the performance benefits of Server Components.
Interview Questions
Client Components should be pushed as far down the component tree as possible (the 'Leaves' of the tree). You want your layout, navigation, and page wrappers to be Server Components to keep the bundle size small, and only use Client Components for the specific interactive elements (like a search bar or a toggle switch).
You cannot import a Server Component directly into a Client Component. However, a Client Component can accept a Server Component passed to it as a children prop. This allows you to wrap Server Components in interactive Client-side layouts (like an animated wrapper) without breaking the Server Component architecture.
Real-World Example
Pushing Client Components to the Leaves: If we had put useState directly inside StorePage.js, we would have been forced to put "use client" at the top of the page. That would force Navbar and ProductGrid to also become Client Components, massively bloating the user's browser download size. By extracting the input into its own file, we preserve the server-side benefits for 95% of the page.
// --- Page.js (SERVER COMPONENT) ---
// This file does all the heavy lifting and data fetching
import { Navbar } from './Navbar';
import { SearchBar } from './SearchBar'; // A Client Component
import { ProductGrid } from './ProductGrid';
export default async function StorePage() {
const products = await db.getProducts();
return (
<div>
<Navbar />
{/* We only make the SearchBar a Client Component, keeping the rest of the page 100% Server Rendered! */}
<SearchBar />
<ProductGrid data={products} />
</div>
);
}
// --- SearchBar.js (CLIENT COMPONENT) ---
"use client";
import { useState } from 'react';
export function SearchBar() {
const [query, setQuery] = useState('');
return <input value={query} onChange={e => setQuery(e.target.value)} />;
}Check Your Knowledge
Test your understanding of Client Components with these quick questions.