Designing Components for Server Boundaries
Learn how to reason about component design when rendering spans server and client environments, and how those boundaries determine data flow, performance, and UI stability.
In traditional React applications, component design is mostly a local concern. We decide how to split components based on readability, reuse, or separation of concerns, assuming everything ultimately runs in the browser. When performance problems appear, slow pages, heavy bundles, awkward loading states. We usually try to fix them inside that model: memoization, code splitting, conditional rendering, or more granular loading spinners.
As applications grow, this approach starts to break down. A single component may need data, layout, and interactivity, but those responsibilities pull in opposite directions. Fetching data early improves perceived performance, but it also couples the component to network latency. Adding interactivity forces the entire component into the client bundle, even if most of its output is static. To compensate, we often add flags, skeletons, and defensive logic, but the UI still feels unstable, content flashes, layouts shift, and transitions feel arbitrary.
React 19 changes the terrain entirely by allowing components to render on the server by default. This is not just a performance feature; it introduces multiple rendering environments with different costs and capabilities. The problem is that many existing component designs assume a single environment. When those designs are carried forward unchanged, we accidentally ship too much code to the client, block streaming, or reintroduce waterfalls we thought we had eliminated. Server boundaries exist to solve these problems, but only if we redesign components with those boundaries in mind.
The diagram shows a UI tree starting with a top-level page component rendered on the server. That page splits into several regions: a navigation shell, a data-heavy content section, and a small interactive control panel. The navigation and content are rendered entirely on the server and stream progressively. Inside the content region, a clear boundary marks where a client component begins receiving prepared data as props and handling user interaction without re-fetching or restructuring the page. The diagram highlights that data flows downward across the boundary, while interactivity stays contained below it.
Server boundaries and rendering coordination
The key shift is realizing that component boundaries are no longer just about composition; they are about execution context. Every component implicitly answers the question: Where does this logic run, and what does it force React to wait for?
A server boundary marks a transition between environments. Above the boundary, components can fetch data directly, render without adding to the client bundle, and participate in streaming. Below the boundary, components can hold state, ...