Server vs Client Components
Server Component | Client Component | |
|---|---|---|
Where it renders | Server only (build time or request time) | Server (initial HTML) and browser (hydration + updates) |
Can use hooks / state | No — no | Yes |
Can use event handlers | No — no | Yes |
Can access backend resources directly | Yes — databases, filesystem, secrets, internal services | No — must go through an API route or a Server Action |
Ships JavaScript to the browser | No, for its own code | Yes — its code and dependencies are bundled and sent |
Ideal for | Data fetching, layout, static content, anything non-interactive | Forms, toggles, counters, anything needing browser state or APIs |
The general guidance
'use client' to the specific components that genuinely need interactivity, hooks, or browser APIs — and when you do, push that boundary as far down the component tree as possible, onto small "leaf" components, instead of marking an entire page or layout as a Client Component.Worked example — one small Client Component, not a whole client page
Consider a product page: a title, a description, pricing info fetched from a database, and a "wishlist" heart button that toggles on click. Only the button needs interactivity.
components/WishlistButton.tsx — the one interactive leaf
'use client'
import { useState } from 'react'
export default function WishlistButton({ productId }: { productId: string }) {
const [saved, setSaved] = useState(false)
return (
<button onClick={() => setSaved((prev) => !prev)}>
{saved ? '♥ Saved' : '♡ Save for later'}
</button>
)
}app/products/[id]/page.tsx — everything else stays a Server Component
import { db } from '@/lib/db'
import WishlistButton from '@/components/WishlistButton'
export default async function ProductPage({
params,
}: {
params: { id: string }
}) {
// Runs on the server: direct DB access, no client-side fetch needed.
const product = await db.product.findUnique({ where: { id: params.id } })
return (
<article>
<h1>{product.name}</h1>
<p>{product.description}</p>
<p>${product.price}</p>
{/* Only this one component ships JS to the browser. */}
<WishlistButton productId={product.id} />
</article>
)
}'use client' directive. It fetches data directly from the database and renders static markup for the title, description, and price — none of that requires a single byte of client-side JavaScript. Only WishlistButton hydrates in the browser, and it's a tiny fraction of the page's total code.Default to Server Components; they are cheaper (no JS shipped) and can access backend resources directly.
Add
'use client'only to components that specifically need hooks, event handlers, or browser APIs.Push the client boundary down to small, focused leaf components rather than marking whole pages/layouts as client.
A Server Component page can freely import and render Client Components — this is the standard, recommended composition.