Two Kinds of Components Now Exist
Modern React (used through frameworks like Next.js) distinguishes between Server Components, which render on the server and send only the resulting HTML/data to the browser, and Client Components, which render in the browser and can use state, effects, and browser APIs.
Server vs Client Components
| Server Component | Client Component | |
|---|---|---|
| Runs where | On the server only | In the browser (and initially on the server for SSR) |
| Can use useState/useEffect | No | Yes |
| Can access the filesystem/database directly | Yes | No |
| Ships JavaScript to the browser | No (zero JS for this component) | Yes |
| Declared with | The default — no directive needed | "use client" at the top of the file |
A Server Component (Default)
// This runs only on the server — its code never ships to the browser.
async function ProductList() {
const products = await db.query("SELECT * FROM products");
return (
<ul>
{products.map((p) => <li key={p.id}>{p.name}</li>)}
</ul>
);
}A Client Component
"use client";
import { useState } from "react";
function LikeButton() {
const [liked, setLiked] = useState(false);
return (
<button onClick={() => setLiked(!liked)}>
{liked ? "❤️ Liked" : "🤍 Like"}
</button>
);
}Why This Split Exists
Server Components can fetch data directly (no client-side API round trip needed) and never add to the JavaScript bundle sent to the browser. Client Components are still necessary for anything interactive — state, event handlers, effects, browser-only APIs.
Composing Server and Client Components
A Server Component can render a Client Component as a child, passing serializable data as props. A Client Component, however, cannot import and directly render a Server Component — data flows from server to client, not the other way around.
A Server Component Rendering a Client Component
// page.jsx — Server Component (no "use client")
import LikeButton from "./LikeButton"; // Client Component
async function ProductPage({ params }) {
const product = await getProduct(params.id);
return (
<div>
<h1>{product.name}</h1>
<LikeButton /> {/* interactive island inside a server-rendered page */}
</div>
);
}Props Must Be Serializable
Since Server Components pass data to Client Components across the server/client boundary, props must be serializable (plain objects, strings, numbers, arrays) — you can’t pass a function or a class instance from a Server Component down to a Client Component.
This is a Framework Feature
Server Components require a framework with the right build/runtime support — Next.js’s App Router is the most common way to use them today. Plain React (via Vite, for example) doesn’t have Server Components without such a framework.
Best Practice
Default to Server Components, and mark a component "use client" only when it actually needs interactivity (state, effects, event handlers) or browser-only APIs — pushing "use client" as far down the tree as possible keeps the client JavaScript bundle smaller.