skip to content

In a React 19 app that uses React Server Components, what is the fundamental difference between a Server Component and a Client Component?

level: juniorimportance: must knowfreq 78%

answer

  1. two runtimes, one component tree
  2. one side never ships any code
  3. state and handlers need the browser
  4. server is default, client is opt-in

basics

~20 s

Server Components execute only on the server, once per request, and their code never ships to the browser. Client Components are sent to the browser as JavaScript, where they mount, hold state, and respond to user input.

solid answer

~40 s

A Server Component runs on the server while the request is being handled. It produces a description of the UI that React sends to the browser, and its own code — plus the libraries it imports — never lands in the client bundle. Because it runs once and is then gone, it cannot hold state, run effects, or attach event handlers. A Client Component is the other side: its module is marked with `'use client'`, its code is shipped and executed in the browser, and that is what buys you `useState`, effects, refs and DOM event handlers. In an RSC app the default is server, and you opt into client only where interactivity or browser APIs are genuinely needed.

code

jsx · 12 lines
jsx
import AddToCart from './add-to-cart.jsx';

export default async function ProductPage({ id }) {
  const product = await db.products.find(id);
  return (
    <article>
      <h1>{product.name}</h1>
      <p>{product.description}</p>
      <AddToCart productId={product.id} />
    </article>
  );
}

go deeper

for a junior

Be able to state the split cleanly: Server Components run on the server and their code never reaches the browser; Client Components ship, mount, and can use state and event handlers. Name one example of each.

for a middle

Explain what follows from the split — why no hooks or handlers on the server side, what stays out of the bundle, and that server is the default while 'use client' is the opt-in.

for a senior

Show you can sort a real feature list into the two buckets and defend each call on bundle cost, data proximity, and secret handling, rather than reciting the definition.

for a principal

Own the tradeoff: what the boundary costs a team in cognitive load and debugging, when the payoff in bytes and data locality is real, and when an app is better off staying fully client-rendered.

## One tree, two runtimes Before React Server Components, every component you wrote had exactly one nature: it was JavaScript that shipped to the browser, mounted, and could re-render. Server-side rendering did not change that — it just ran the same component once on the server to produce initial HTML, and then shipped the identical code down so the browser could take over. RSC splits components into two kinds that live in the *same* tree but execute in different places. Server Components run only on the server (at request time, or at build time depending on the framework). Client Components run in the browser, and their code is part of the bundle the user downloads. ## What a Server Component is A Server Component is an ordinary function component with no directive at the top of its module — in an RSC app, server is the default. What makes it special is where it runs and how long it lives: - It executes once, during the server render, and produces a serialized description of the UI it rendered. React sends that description to the browser, which turns it into DOM. - Its source code and every module it imports stay on the server. A 300 kB Markdown renderer imported by a Server Component costs the user zero bytes. - It can reach server-only resources directly: a database client, the filesystem, an internal service that requires a secret from an environment variable. - It can be an `async function` and `await` inside its body, because the server render is allowed to wait. And what it *cannot* do follows from the same facts: no `useState`, no `useReducer`, no `useEffect` or `useLayoutEffect`, no refs into the DOM, no `window` or `document`, no `onClick` on the elements it renders, and no context. There is no instance of it in the browser for any of those to act on. ```jsx // No directive — this is a Server Component. export default async function InvoicePage({ id }) { const invoice = await db.invoices.find(id); // never reaches the browser return <h1>Invoice #{invoice.number}</h1>; } ``` ## What a Client Component is A Client Component is a module whose first line is the `'use client'` directive. Its code is compiled into the client bundle, and in the browser it behaves exactly like the React you already know: it mounts, holds state, runs effects, attaches handlers, and re-renders whenever its state or props change. Note the asymmetry that trips people up: "Client Component" does not mean "never rendered on the server". In a server-rendered app, React also runs Client Components on the server during the HTML pass so the user sees content before any JavaScript loads. What distinguishes them is that they *also* run in the browser and stay alive there. A Server Component runs on the server and nowhere else, ever. ```jsx 'use client'; import { useState } from 'react'; export default function Quantity() { const [n, setN] = useState(1); // needs a live instance in the browser return <button onClick={() => setN(n + 1)}>{n}</button>; } ``` ## How they compose A Server Component can render a Client Component as part of its output — that is the normal direction and how interactivity gets onto a server-rendered page. The props it passes across that seam have to be serializable, since they travel over the wire as data. The practical mental model an interviewer wants to hear: *server by default, client where the browser is genuinely required*. Fetching and shaping data, reading secrets, rendering static markup, and using heavy formatting or parsing libraries are server work. Anything driven by state, an event handler, a ref, a browser API, or a subscription is client work. ## Why the split exists Two payoffs, and you should be able to name both. First, bundle size: the code that only shapes UI on the way out never becomes JavaScript the user downloads, parses, and executes. Second, data access: a Server Component sits next to the data, so it can read it directly instead of the browser making a round trip to an endpoint that exists only to feed the component. The cost is a new boundary in your architecture, and a new class of mistakes — assuming a component is interactive when it is not, or dragging a large subtree into the client bundle without meaning to. ## Common confusions - **"It's just SSR renamed."** No. SSR renders client components early; RSC removes components from the client entirely. - **"Server Components re-render when state changes."** They cannot. Nothing in the browser can re-run them; new server output requires a new server render. - **"You must label every component."** Only client modules carry a directive; server is the default in an RSC app.

  • If Server Components never ship JavaScript, why does the browser still download React in most RSC apps?
    Because nearly every real app has some Client Components, and those need React's client runtime to render and stay interactive. The Server Component's own code and its imports stay off the bundle, but the runtime plus every client module still ships. A page with genuinely zero Client Components can ship no component JavaScript at all.
  • Does a Server Component re-render when the user interacts with the page?
    No. It ran once on the server and there is no instance of it in the browser to update. Interaction re-renders Client Components only. Fresh server output requires a new server render — a navigation or an explicit refresh in your framework — which sends down a new payload; a click cannot re-run server code by itself.

saying these in an interview costs you the question

  • Says Server Components are just SSR with a new name
  • Claims Client Components never execute on the server
  • Believes Server Components re-render when state changes
  • Assumes every component must be labelled server or client
  • Thinks a Server Component can attach an onClick handler

context