What are React Server Components (RSC), how do they differ from Client Components and from traditional SSR, and what are the rules for mixing them?
Grasping the newest architectural shift: where a component runs and what that lets it do or forbids.
Traditional SSR renders your whole app to an HTML string on the server, ships it, then *hydrates* — the same component code also runs on the client and all its JS is shipped. Server Components are a different axis: components that run *only* on the server and never ship their JS to the browser at all. Their rendered output is serialized and streamed to the client, which merges it into the tree. Benefits: zero bundle cost for server-only code, direct access to server resources (read a file, hit the database, use secrets) right inside the component with async/await, and less client JS overall. The constraints follow from 'this never runs in the browser': Server Components can't use state, effects, or event handlers (no useState/useEffect/onClick) and can't use browser-only APIs. Anything interactive must be a Client Component, marked with the `'use client'` directive at the top of the file, which opts that module (and its imports) into the client bundle. Composition rules: Server Components can import and render Client Components, but not vice versa — instead you pass Server Components *into* Client Components as children/props (a Client Component can render `{children}` that were produced on the server). Props crossing the boundary must be serializable (no functions). This is the model frameworks like Next.js App Router are built on; RSC and SSR are complementary, not competing.
Static/data-heavy UI with server resource access and no interactivity → Server Component; anything with state/effects/handlers → Client Component ('use client').
// app/page.jsx — Server Component (default, no directive). Runs on server only.
async function Page() {
const posts = await db.query('SELECT * FROM posts'); // direct DB access, no API layer
return <Feed posts={posts} />; // Feed can be a Client Component receiving serializable props
}
// Feed.jsx
'use client'; // opts this module into the client bundle
export default function Feed({ posts }) {
const [open, setOpen] = React.useState(false); // state is allowed here
return <button onClick={() => setOpen(o => !o)}>{posts.length} posts</button>;
}