Server Components Theory
React Server Components split rendering between server and client. Server Components run only on the server, ship zero JavaScript to the browser, and can access backend resources directly. Client Components handle interactivity.
What it is
Server Components are React components that execute exclusively on the server. They can use async/await, access databases and file systems directly, and their code never reaches the browser bundle.
Client Components are marked with 'use client' at the top of their file. They run in the browser (and optionally on the server for SSR) and can use hooks like useState, useEffect, and event handlers.
The RSC protocol serializes the server render output into a streaming payload: HTML chunks for server-rendered content, client references for 'use client' components, and serialized props at the boundary between server and client.
Interview framing: define Server Components in one sentence, then explain one concrete runtime behavior and one common pitfall with a short code example.
How it works
- Step 1: React starts a server render pass. It calls Server Component functions on the server, executes async operations, and produces rendered output.
- Step 2: when React encounters a 'use client' component, it stops rendering and records a client reference (module path + export name) in the payload.
- Step 3: props crossing the server/client boundary are serialized. Only JSON-serializable values (strings, numbers, booleans, arrays, plain objects, Promises) can cross. Functions, classes, and Symbols cannot.
- Step 4: the RSC payload streams to the browser. The client runtime loads the referenced client modules and hydrates them with the serialized props.
- Step 5: Server Components that were passed as children to Client Components (the composition pattern) are already rendered to HTML on the server. The client component receives them as opaque JSX it can render without re-executing.
Common mistakes
Importing a Server Component into a Client Component
Once you cross the 'use client' boundary, everything imported by that module becomes part of the client bundle. You cannot import a Server Component into a Client Component file.
Fix: Use the composition pattern: pass the Server Component as children or a prop from a Server Component parent. The parent renders it on the server and passes the result down.
Passing non-serializable props across the boundary
Functions, class instances, Symbols, and other non-serializable values cannot cross the server/client boundary. Attempting to pass them throws a serialization error.
Fix: Pass only serializable data (strings, numbers, booleans, arrays, plain objects). If the client needs a callback, define it in the Client Component itself, not on the server.
Adding 'use client' too high in the tree
Marking a component as 'use client' turns it and all its imports into client code. This increases the JavaScript bundle and prevents server-only features (async, direct DB access) in the entire subtree.
Fix: Push the 'use client' boundary as far down the tree as possible. Only the interactive leaf components need it. Use composition to keep data-fetching in Server Components.
Interview questions
Common interview prompts with concise model answers.
What is the difference between Server Components and SSR?
SSR renders the full component tree on the server to produce initial HTML, then ships all the JavaScript to the client for hydration. Server Components render specific components on the server permanently - their code never reaches the client bundle. SSR is a rendering strategy; Server Components are a component type.
Can a Client Component render a Server Component?
Not by importing it directly. But a Server Component can be passed to a Client Component as children or a prop (the composition pattern). The Server Component is rendered on the server first, and the Client Component receives the result as opaque JSX.
// ServerParent.tsx (Server Component)
export default function ServerParent() {
return (
<ClientShell>
<ServerSidebar /> {/* rendered on server, passed as children */}
</ClientShell>
);
}What can cross the server/client boundary?
Only JSON-serializable values: strings, numbers, booleans, null, arrays, plain objects, Dates, and Promises (when used with the use() hook). Functions, class instances, Symbols, and DOM nodes cannot be serialized and will throw an error.
Why do Server Components reduce bundle size?
Server Component code runs only on the server. Their imports (database drivers, markdown parsers, large data processing libraries) are never sent to the browser. Only the rendered output (HTML) and client references are included in the payload.
Related concepts
Continue with these concepts to strengthen your mental model.