Skip to main content

Server Components

React Server Components (RSC) run only on the server: their code never reaches the browser, they can be async and read a database or secrets directly. In the App Router every component is a Server Component unless it is under a 'use client' boundary. The server renders them into the RSC Payload, a serialized tree that also holds references to Client Components; Next.js then turns it into HTML. RSC is not the same as SSR: SSR is about producing HTML, RSC is about where a component runs.

Applies to: Next.js 16.4, React 19.3.

Details​

RSC vs SSR​

Server Components (RSC)SSR
What it isA kind of componentA way to produce the initial HTML
JavaScript in the browserNone for these componentsThe code of all components, for hydration
OutputRSC Payload (and HTML from it)HTML
On client navigationRendered again on the server; payload is sentNot involved: the client renders
HydrationNot hydratedHydrated

Client Components are rendered to HTML on the server too (this is SSR) and then hydrated. A Pages Router app has SSR but no RSC.

What Server Components Cannot Do​

  • Use state, effects or other client hooks (useState, useEffect, useReducer), and context (useContext).
  • Have event handlers (onClick, onChange).
  • Use browser APIs (window, localStorage).
  • Pass functions to Client Components as props, except Server Actions: props must be serializable.

For any of that, move the part into a Client Component.

How a Route Is Rendered​

On the server:

  1. React renders Server Components into the RSC Payload.
  2. Next.js uses the payload and the Client Component code to render HTML.

In the browser:

  1. The HTML is shown immediately, not yet interactive.
  2. The RSC Payload is used to reconcile the Server and Client Component trees.
  3. Client Components are hydrated and become interactive.

Hydration​

  • Hydration attaches event handlers and state to the server HTML; the first client render must match it. Causes of a mismatch and the fixes are in React in the Browser.
  • In Next.js a browser-only part can also be rendered with next/dynamic and ssr: false inside a Client Component.

Rendering Strategies​

  • Static: HTML is made at build time or on revalidation, cached and served to everyone.
  • Dynamic: HTML is made on every request, because the output depends on the request: cookies(), headers(), searchParams, connection().
  • Streaming: the response is sent in parts; each <Suspense> boundary shows its fallback first and is replaced when its content is ready. loading.tsx wraps the page of its segment in such a boundary.

How they combine depends on the caching model:

  • The choice is made per component, not per route (Partial Prerendering). At build time the static markup, the 'use cache' parts and the <Suspense> fallbacks form a prerendered static shell; the parts inside <Suspense> that read request data or uncached data render on request and stream in.
  • Uncached data or request data outside <Suspense> fails the build.
  • See Caching.

Example​

Streaming: the heading arrives at once, each list replaces its fallback when its data is ready. This is how it works with Cache Components. In the previous model the page uses no request data and is prerendered whole at build time; adding await connection() at the top of Posts and Users makes them render on request.

import { Suspense } from 'react';
import { Posts } from './posts';
import { Users } from './users';

export default function Page() {
return (
<main>
<h1>Dashboard</h1>
<Suspense fallback={<p>Loading posts...</p>}>
<Posts />
</Suspense>
<Suspense fallback={<p>Loading users...</p>}>
<Users />
</Suspense>
</main>
);
}

Follow-up Questions​

  • Can a Client Component import a Server Component? No: everything a client module imports becomes client code. Pass the Server Component as children or another prop instead.
  • Are Server Components the same as SSR? No, see the table above. SSR exists without RSC, and RSC also works on client navigation without producing HTML.
  • What does streaming improve? The user sees the page shell and the fast parts sooner instead of waiting for the slowest query.
  • How do you keep server-only code out of the client? Import server-only in the module: importing it from a Client Component becomes a build error.

Common Mistakes​

  • suppressHydrationWarning on a parent element to silence a mismatch: it works only one level deep and hides the bug; move the browser-only read into useEffect.
  • "Server Components replace SSR" or "Client Components render only in the browser": Client Components are prerendered on the server as well.
  • Adding 'use client' to a whole page because of one button: the boundary should be as low in the tree as possible.
  • Making a whole layout a Client Component to add a provider: wrap only {children} in a small provider component, see Client Components.

See Also​