In a React app using Server Components, what does the 'use client' directive at the top of a module actually do to the module graph, and why is it described as a boundary rather than a per-component switch?
answer
- marks a module, not a component
- the effect travels down imports
- entry point into the client bundle
- importers are unaffected; imports are not
- still evaluated during server render
basics
~20 s'use client' marks a module as an entry point into the client bundle: that file and everything it imports become client code. It opens a boundary in the module graph rather than switching one component into client mode.
solid answer
~50 sThe directive is a **module-level marker**, not a component annotation. When the bundler sees `'use client'` at the top of a file, that file becomes an entry point into the client graph: every export in it is a client component, and every module it imports — transitively, including third-party packages — is compiled and shipped as client code. That is why it is a *boundary*: it cuts the graph in two, with server modules above it and client modules below it. Two things it does **not** mean. First, it does not mean "runs only in the browser" — in a framework that server-renders, the module is still evaluated on the server to produce HTML; the directive decides which code ships and where it becomes interactive. Second, it does not affect the module's importers: a server component that renders a client component stays a server component.
go deeper
Know the directive is a bare string at the very top of a file, above the imports, and that it makes everything exported from that file a client component. Say plainly that server components are the default and need no directive.
Explain the transitive part out loud: the marked module plus every module it imports becomes client code, so the directive draws a boundary in the module graph. Be ready to say that it propagates down imports and never up to importers.
Show you reason about placement as a shipping decision — a directive on a layout or a shared entry point drags a whole subtree into the bundle, and the fix is to move it to the smallest interactive leaf. Expect to be asked how you would find such a mistake in a real app.
Own the convention: where boundaries live, who may add a directive to a shared module, and how the team prevents a single high-up marker from quietly converting the app into a client-rendered one. Tie the rule to a measurable budget rather than to taste.
## The one-line definition `'use client'` is a directive — a bare string literal at the very top of a module — that tells an RSC-aware bundler: *from here down, this is client code*. It is not a React API you call, not a component prop, and not a per-component toggle. It annotates a **module**. ```js 'use client'; import { useState } from 'react'; import { formatPrice } from './format'; export function Counter() { const [n, setN] = useState(0); return <button onClick={() => setN(n + 1)}>{formatPrice(n)}</button>; } ``` ## Why it is a boundary and not a switch An RSC application is a single module graph. By default every module in it is a server module: it runs once, during the render on the server, and none of its code is sent to the browser. A `'use client'` directive plants an **entry point** into that graph. From that node downward: - Every export of the marked module is a client component (or a client-side value, if it exports a plain function or constant used by client code). - Every module the marked module imports is client code too — and everything *those* modules import, transitively. A date library, an icon set, a validation package: if it is reachable by imports from a client module, it is compiled into the client bundle. This transitivity is the entire point and the entire hazard. The directive is written once, in one file, but its effect is on a whole *subgraph*. Candidates who read it as "this component is a client component" miss the part that actually shows up in a bundle report. Crucially the effect only propagates **downward**, along imports. It says nothing about who imports the marked module. A server component may freely render `<Counter />`; the server component stays a server component. The boundary is the edge between them, and it is drawn by the directive on the child, never by the parent. ## What the directive does not mean **It does not mean "client-only execution."** In a framework that does server-side rendering, a client module is still evaluated on the server to produce the initial HTML, and then hydrated in the browser. Code that touches `window` or `document` at module scope or during render will still break on the server. The directive controls *which bundle the code ships in and where it can be interactive*, not whether the server ever runs it. **It does not mean "this file has state."** A component with no hooks at all can sit below a client boundary — it is client code because of where it is in the graph, not because of what it does. **It is not the opposite of `'use server'`.** `'use server'` marks server *functions* that a client may call over the network; it does not mark server components (those are the default and need no directive). Confusing the two is one of the most common interview stumbles. ## Placement rules The directive must be the **first statement in the file**, above all imports (comments may precede it). Put it below an import and it is just a stray expression statement with no effect — the file silently stays a server module, and the failure shows up later as "hooks are not supported in server components" or a missing event handler. Because it is per-module, a file that mixes an interactive widget and a heavy server-only data helper cannot have it both ways: splitting such a file is usually the fix. ## The practical consequence Because the marker is transitive, *where* you place it decides how much JavaScript your users download and how much of your tree keeps the server-component benefits — direct data access, zero shipped code, secrets that never leave the server. Placing it high (a layout, a shared barrel) drags the subtree into the browser. Placing it on the smallest interactive leaf keeps the boundary thin. The usual interview follow-up is exactly this: given a page that must be mostly static but has one interactive control, where does the directive go? Answer: on the control, and everything else stays above the line. ## Mental model Think of the graph as a sheet of paper with a line drawn across it. Above the line, code runs during the server render and is never sent. Below the line, code is bundled, sent, and hydrated. `'use client'` is the pen that draws the line, and it is drawn *under* the module that declares it — so the higher you write it, the more paper you hand to the browser.
- If a client component renders a server component that its parent passed as a prop, does the directive turn that child into client code?No. The directive follows `import` edges, not props. A child that was rendered on the server and handed down as an element prop was never imported by the client module, so its code never enters the client bundle — only its already-rendered output crosses the boundary.
- A file has 'use client' written after its import statements. What happens?Nothing good and nothing loud. Directives are only recognised as the leading statement of a module, so the line is parsed as a meaningless string expression and the file remains a server module. The symptom appears later — hooks or event handlers failing in a component that looks correctly marked.
- Does 'use client' mean the component's code never executes on the server?No. In a server-rendering framework the client module is still evaluated on the server to produce initial HTML, then hydrated in the browser. Module-scope or render-time access to `window`, `document`, or `localStorage` still throws during that server pass; it belongs in an effect or a guarded lazy read.
- Is 'use server' simply the inverse of 'use client'?No. Server components are the default and need no directive at all. `'use server'` marks *functions* as server functions that client code may invoke across the network — a different mechanism with different security implications. The pairing of the two names is a naming accident, not a symmetry.
saying these in an interview costs you the question
- Says 'use client' makes a component render only in the browser
- Thinks the directive applies to just that one component
- Believes every file in a client subtree needs its own directive
- Calls server components 'use server' components
- Puts the directive after the imports and expects it to work
- Thinks marking a child forces its server parent to become a client component