Overview
Server Components are stable in React 19: they render on the server, ship no component JavaScript, and may be async. Client Components render in the browser and own state, Effects, and event handlers. The bundler and framework APIs underneath do not follow semver across 19.x minors, so frameworks pin a React version. For selection rules see server-vs-client-components; for the framework see nextjs.
Server by default, "use client" at the edges
In an RSC framework a file is a Server Component unless it starts with "use client". The directive marks a module and everything it imports as client code; it describes the module graph, not the render tree. There is no directive for Server Components, and "use server" marks Server Functions only. Put the directive on the smallest interactive leaf. Wrapping a whole page in "use client" ships everything below it to the browser.
// app/page.tsx: Server Component
import { CartButton } from "./cart-button"; // "use client" file
export default async function Page() {
const products = await db.product.findMany();
return <main><ProductList products={products} /><CartButton /></main>;
}Pass only serializable props across the boundary
Props from a Server to a Client Component must serialize. Allowed: primitives (including bigint), Symbol.for symbols, strings, arrays, Map, Set, typed arrays, Date, plain objects, Promises, Server Functions, and JSX elements. Not allowed: ordinary functions, class instances, null-prototype objects, and symbols not made with Symbol.for.
<ClientFilter onChange={(v) => log(v)} /> // Wrong: plain function
<ClientFilter onChange={logAction} /> // Right: a "use server" functionStream data to a client component with a promise and use()
Create the promise on the server, do not await it, and unwrap it in the Client Component. The promise is stable across re-renders; a promise created inside a Client Component render is recreated every time.
// Server Component
const comments = getComments(id);
return <Suspense fallback={<Skeleton />}><Comments commentsPromise={comments} /></Suspense>;
// Client Component
"use client";
function Comments({ commentsPromise }: { commentsPromise: Promise<Comment[]> }) {
return use(commentsPromise).map((c) => <p key={c.id}>{c.text}</p>);
}Fetch in the component that renders the data
Colocate the await with the component that needs the data and skip prop drilling. Wrap loaders that do not use fetch (ORM, SDK calls) in React cache() so two components asking for the same record in one request run the query once. Next.js also memoizes fetch GET calls within a render. Wrap slow fetching components in <Suspense> so the shell streams first; see react-suspense.
Keep client shells thin by passing server content as children
A Client Component cannot import a Server Component, but it can receive one as children or a named prop. The shell owns the interactive state and the heavy content stays on the server.
// Server Component
<Tabs tabs={tabs}>{await renderActivePanel()}</Tabs>Avoid the common boundary mistakes
- Import
server-onlyin modules that touch secrets,fs, or a database driver, so a client import fails the build. - Read cookies, headers, and environment variables in a Server Component and pass the values down.
- Do not wrap a fetch in a Client Component for a loading state; fetch on the server and suspend the parent.
- A file with
useStateoruseEffectneeds"use client"; the framework rejects it otherwise.
Keep RSC packages patched
React Server Components had a critical unauthenticated remote code execution flaw (CVE-2025-55182, CVSS 10.0, disclosed 2025-12-03), then denial-of-service (CVE-2025-55184, CVE-2025-67779, CVE-2026-23864) and source-exposure (CVE-2025-55183) flaws. They affect react-server-dom-webpack, -parcel, and -turbopack 19.0 through 19.2.3; apps with no RSC framework or bundler plugin are not affected. Run 19.0.4, 19.1.5, or 19.2.4 or later: the first fixes (19.0.1 to 19.0.3, 19.1.2 to 19.1.4, 19.2.1 to 19.2.3) are incomplete. Upgrade the framework as well (Next.js, Waku, React Router RSC, Parcel, Vite RSC), because frameworks that vendor these packages need their own patched release.