Server Components
Components that never ship to the browser — a different mental model, not just an optimisation.
50 minDifficulty 4/5nextjs · reactAI-writtenWritten by a model on 16 August 2026 and not yet read by a person. Checked automatically: schema, the pedagogical rules the hand-written material is held to, and every diagram parsed for real.
Before this
Why this exists
A component that fetches a blog post and renders its markdown never needs `useState`, never needs to run in the browser at all, and yet the traditional model ships its full JavaScript to every visitor anyway. Server Components aren't a performance trick bolted onto that model — they're a genuinely different kind of component that simply never exists in the browser's JavaScript at all.
The mental model
A traditional React component is a function that runs wherever React runs — server for the first render maybe, then again in the browser. A Server Component runs EXCLUSIVELY on the server, once, and its output — HTML plus a compact description of any embedded Client Components — is what's sent down. It has no re-render, no state, no lifecycle in the browser, because it never executes there at all.
How it works
Server Components can be async and use await directly in the component body
`async function Page() { const data = await fetchData(); return <div>{data.title}</div>; }` is valid syntax for a Server Component, because the whole component runs to completion on the server before anything is sent — there's no rendering-while-fetching problem to solve, since the fetch simply finishes before the HTML exists at all.
Server-only code and secrets never reach the client bundle
A Server Component can import a database driver, use an API key from an environment variable, or run filesystem operations — none of this code is ever bundled for the browser, because the component never runs there. This is a genuine security boundary, not just a size optimisation: a secret used only in a Server Component structurally cannot leak into client-visible JavaScript.
Server Components can render Client Components, but not vice versa (directly)
A Server Component can import and render a Client Component as a child — this is the common, expected pattern. A Client Component CANNOT directly import a Server Component the same way, because by the time it's running in the browser, the server-only code inside that Server Component (a database call, say) has nowhere to execute. The workaround is passing a Server Component as a `children` prop from above, composing them rather than importing across the boundary.
Props passed from a Server Component to a Client Component must be serializable
A Server Component's output crosses a network-like boundary to reach the client — so anything passed as a prop into a Client Component must be serializable to that format: plain objects, arrays, strings, numbers, and a few special cases, but NOT functions, class instances, or a database connection object. This is a genuinely new class of prop-type error that didn't exist before this boundary existed.
The mechanism
The server renders a Server Component tree to completion, including awaiting any data it needs, producing a special serialized format (not plain HTML strings, but a description React can reconstruct) that includes placeholders for any embedded Client Components. The client receives this payload, renders the static parts directly, and hydrates the Client Component placeholders with their actual JavaScript, wiring up interactivity only where it was explicitly requested.
What people get wrong
- Server Components are just Server-Side Rendering (SSR) with a new name.
- SSR renders a traditional component to HTML on the server for the FIRST paint, but that same component's JavaScript still ships and re-runs in the browser afterward (hydration); a Server Component's code never ships to the browser at all, ever, under any circumstance. Conflating the two obscures the actual security and bundle-size benefit — a Server Component's server-only imports (a database driver, a secret key) genuinely cannot end up in the client bundle, which isn't true of an SSR'd component whose code still ships.
- You can freely import a Server Component from inside a Client Component's file, same as any other component.
- A Client Component cannot directly import and render a Server Component, because once it's executing in the browser, there's no server available to run the Server Component's server-only logic — the correct pattern is composing them via props (like `children`) from a Server Component ABOVE both. Hitting this restriction is a common early confusion, and understanding the 'composition, not direct import, across the boundary' pattern is the actual fix, not a workaround to route around.
- Any prop value can be passed from a Server Component to a Client Component, same as normal React props.
- Only serializable values cross this boundary — functions, class instances, and other non-serializable values passed as props to a Client Component will fail or behave unexpectedly, because the value has to survive being converted to the special server/client payload format. This produces a genuinely new category of bug (a callback function silently not working as a prop from server to client) that doesn't have an equivalent in the pre-Server-Components model.
When not to use it
- You need to pass an event handler down to something rendered from the server.
- Define the event handler INSIDE a Client Component, not pass a function prop across the server/client boundary — the interactivity has to originate from within already-client code.
- A component needs both server-only data access AND client-side interactivity in the same visual unit.
- Split it: a Server Component fetches the data and passes serializable results as props to a nested Client Component that handles the interactive parts — composition across the boundary, not one component trying to be both.
Terms
- Server Component
- — A component that renders exclusively on the server, never shipping its code to the browser, with no client-side re-render or lifecycle.
- Client Component
- — A component marked with 'use client' that renders initial HTML on the server but also ships its JavaScript to hydrate and become interactive in the browser.
- Serializable props
- — Values (plain objects, arrays, strings, numbers) that can survive being sent across the server-to-client boundary; functions and class instances cannot.
- Composition across the boundary
- — The pattern of passing a Server Component as a prop (like children) into a Client Component from a shared parent, since a Client Component cannot directly import a Server Component itself.
In an interview
Why can't you directly import and render a Server Component from inside a Client Component's file?
- a Client Component's code runs in the browser, where there's no server-side execution context
- a Server Component may rely on server-only resources (database, filesystem, secrets) that don't exist client-side
- the correct pattern is composing them from a shared Server Component parent, passing the Server Component down as a prop like children
Can you recall it?
How does a Server Component differ from a traditionally server-side-rendered (SSR) component, given that both produce HTML on the server?