Modern React architecture

React Server Components Explained

Understand server and client component boundaries, rendering payloads, serialization, interactivity, and data access tradeoffs.

How this page is maintained

Written for learners, checked against the sources below, and reviewed every quarter. Last reviewed July 27, 2026.

Short answer

React Server Components execute in a server environment and send rendered component output rather than their component code to the browser. In the Next.js App Router, pages and layouts are Server Components by default. Client Components form explicit browser module boundaries for state, events, effects, and browser APIs. The two can be composed, but values crossing into client props must be serializable.

Who this is for: React and Next.js developers deciding which interface code should execute on the server and which must ship to the browser.

  • Keep data access and noninteractive rendering on the server when that improves security, payload size, or locality.
  • Place the client boundary around the smallest coherent interactive area instead of marking an entire page by habit.
  • Treat server rendering, caching, authorization, and browser interactivity as separate decisions that require explicit tests.

Server Components produce a rendering payload

A Server Component runs in the server environment during rendering. It can read data near its source and use server-only credentials without placing those credentials in browser JavaScript. React produces a Server Component payload describing rendered output and references to Client Components. Next.js combines that payload with Client Component code to produce the initial interface.

This is not merely traditional HTML templating, and it does not mean the entire route lacks client JavaScript. On first load, HTML gives an immediate view, the Server Component payload reconciles the component trees, and JavaScript hydrates Client Components. Later navigations can request and apply a new payload without downloading Server Component implementation code.

Client boundaries carry interactivity

A file marked use client declares a boundary into the client module graph. State hooks, event handlers, effects, custom browser hooks, localStorage, geolocation, and other browser-only APIs belong beyond such a boundary. Imports pulled into that graph contribute to the browser bundle, so a high boundary can send much more code than one focused interactive control.

The boundary does not require every visual child to become client code. A Server Component can be rendered on the server and passed as children or another prop into a Client Component slot. However, a Client Component cannot import a Server Component as ordinary client code. Composition direction and module imports are different ideas.

Data crosses through serializable props

A Server Component may pass data to a Client Component, but React must serialize that value into the rendering payload. Plain data fits naturally. Functions, open database handles, and arbitrary class instances do not cross as ordinary props. Keep server capabilities behind server code and pass the smallest safe value the interactive component requires.

Fetching on the server can reduce client waterfalls and keep secrets out of the browser, but it does not authorize data automatically. Check the current user and resource permission before reading sensitive records. Also decide caching deliberately. A server-rendered result can still be stale or shared incorrectly if cache scope and invalidation do not match the data's privacy and freshness needs.

Use the boundary as an architecture tool

Start with the route as server-rendered, then identify true browser interactions. A product detail can remain server code while a quantity selector and add button form a small client island. A highly interactive editor may justify a larger client subtree. The goal is not the fewest directives; it is a clear ownership boundary with acceptable user experience and maintenance cost.

Test initial HTML, subsequent navigation, loading and error states, serialized data, and interactive hydration. Use browser bundle analysis when payload size matters. Framework behavior can change, so verify details against the installed Next.js documentation and current application configuration rather than copying older examples. In current Next.js 16 guidance, asynchronous route params are awaited before use.

Split an account summary page

An account page displays private billing data, a static plan explanation, and an interactive invoice filter.

  1. Keep the page as a Server Component and authenticate and authorize billing access before querying the account records.
  2. Render the plan and initial invoice list on the server, passing only safe invoice display fields to the interactive filter.
  3. Create a focused Client Component for filter state and events rather than marking the whole account page as client code.
  4. Use a server request for filters that need fresh protected data, with validated parameters and another authorization check.
  5. Test the initial response, client hydration, private cache isolation, later filtering, and an expired session.
Result: Sensitive data access remains on the server, the browser receives only the interactive code it needs, and authorization is enforced on every data path.

Server-client boundary map

Mark each component subtree with its execution needs before adding a client directive.

  • Server needs: protected data, secrets, filesystem or database access, server-only library, and cache policy.
  • Client needs: state, events, effects, browser API, custom client hook, and immediate interaction.
  • Crossing data: serializable props, sensitivity, size, freshness, and validation requirements.
  • Composition: imported client subtree, server-rendered slot, loading state, error state, and navigation behavior.
  • Verification: authorization, cache isolation, initial HTML, hydration, bundle size, and subsequent navigation.

Common mistakes

  • Adding use client to a route solely because one nested button needs a click handler.
  • Passing server credentials or an unfiltered private record into serializable client props.
  • Assuming server execution makes a data read authorized or correctly cached without explicit policy.

Try one

A dashboard page fetches private reports and contains one date picker. Propose component boundaries and explain what must be tested.

A strong answer keeps the page and protected report fetch on the server, places the date picker and immediate interaction in a small Client Component, and passes only serializable safe values. A changed date can request server-rendered or API-backed data with fresh authorization. Tests cover cache isolation, initial output, hydration, invalid dates, expired access, and later navigation.

Sources

Learn this with a tutor

Tell LearnLive what you already know and what you need to do with react server components.

Build this course