React Server Components definition
React Server Components (RSC) are React components that run only on the server, fetching data and rendering UI there, and send the result to the browser without shipping their JavaScript code. They work alongside client components, which handle interactivity, so apps load less JavaScript while keeping React's component model. The Next.js App Router popularized them.
How do React Server Components work?
A server component is an ordinary React component that runs on the server, and it can be async: it can query a database or call an internal service directly, then return JSX. React renders the tree on the server into a compact serialized format, often called the RSC payload, and streams it to the browser, which turns it into UI. The server component's code and its dependencies never reach the client bundle.
Interactive pieces are client components, marked with the "use client" directive at the top of the file. They behave like traditional React components with state, effects and event handlers, and they are hydrated in the browser. Server components can render client components and pass them serializable props or children, but client components cannot import server components directly.
Server components vs client components
The rule of thumb is simple: keep components on the server by default and add "use client" only where the browser is genuinely required. Placing that boundary low in the tree, around a button rather than a whole page, keeps the JavaScript bundle small.
- Server components: data fetching, access to secrets and databases, heavy libraries such as Markdown or syntax highlighting with no bundle cost.
- Server components cannot use state, effects, event handlers or browser APIs.
- Client components: useState, useEffect, event handlers, animations and browser APIs such as localStorage.
- Client components still render to HTML on the server first when used with SSR.
React Server Components vs SSR
Server-side rendering turns components into HTML for the first load, then ships all their JavaScript so the browser can hydrate everything. Server components go further: they never hydrate, and their code never ships. The two work together. A framework renders server components to a payload, renders client components to HTML with SSR, and streams both, with Suspense boundaries letting slow sections arrive later. Server Functions, marked with "use server", handle form submissions and other mutations.
Benefits and pitfalls
The benefits are smaller bundles, data fetching next to the data source, no separate internal API for simple reads and better security, since credentials stay on the server. Pages with lots of static content, such as documentation, blogs and product details, gain the most.
The pitfalls come from the new boundary. Props passed to client components must be serializable, a misplaced "use client" can pull a large tree into the bundle, and sensitive fields can leak if a whole database record is passed to a client component. Caching rules add complexity, and sequential awaits can create slow request waterfalls.
Example: a product page in Next.js
A product page is a server component that loads the product, price and reviews from the database and renders the description with a Markdown library. Only the image carousel and the add-to-cart button are client components. The browser downloads JavaScript for those two pieces alone, while the rest arrives as ready-made UI. Nexzem builds Next.js applications this way, placing client boundaries deliberately and checking bundle sizes in CI.