SyncTrix logoSyncTrix
All articles
Platform10 min read

React Server Components in practice: the mental model that makes them click

RSC is not SSR with extra steps. Here's the actual boundary model, the mistakes that erase the benefit, and when the complexity is worth it.

By Priya Iyer
React Server Components in practice: the mental model that makes them click

React Server Components confuse experienced React developers more than beginners, because the existing mental model actively gets in the way. This is not server-side rendering with better ergonomics. It is a different execution model where some components never reach the browser at all, and understanding that distinction is what separates teams getting real gains from teams adding complexity for nothing.

01The distinction that matters

Server-side rendering runs your components on the server to produce HTML, then ships the same component code to the browser to hydrate and make it interactive. The code runs twice and the browser downloads all of it.

Server Components run on the server and their code never ships. The output is a serialised description of the rendered result, not the component itself. A component using a large date formatting library adds nothing to the client bundle, because the library stayed on the server. That is the actual benefit, and it is a bundle-size benefit before it is a performance benefit.

02The boundary is one-directional

Server Components can render Client Components. Client Components cannot import Server Components. This asymmetry is the rule people fight hardest, and fighting it is how the benefit gets erased.

You can, however, pass a Server Component to a Client Component as a child or prop. The server renders it and passes the result through, so an interactive client-side layout can still contain server-rendered content. This composition pattern resolves most situations where the constraint initially feels impossible.

  • Server Components may render Client Components directly.
  • Client Components may receive Server Components as children or props.
  • Props crossing the boundary must be serialisable - no functions, no class instances.
  • 'use client' marks an entry point, not a single file.

03The mistake that erases the benefit

Adding 'use client' to the top of a component does not mark just that file. It marks an entry point into client-land, and everything that component imports becomes client code too. Placing it high in the tree - a layout, a provider, a shared wrapper - converts your entire application to Client Components while appearing to use the new architecture.

The fix is pushing the directive as far down as possible. A page does not need to be a Client Component because it contains a button. Make the button a Client Component and leave the page on the server. In practice this means small islands of interactivity within server-rendered structure.

04Data fetching becomes local again

The most immediately pleasant consequence is that components fetch their own data directly, with async/await, without a client-side data layer. No loading state machine, no cache invalidation library, no prop drilling from a page-level fetch.

This resurrects the waterfall problem in a new form, though. Sequential awaits in nested components serialise round trips. Fetch in parallel where the requests are independent, and use Suspense boundaries deliberately so slow sections stream in without blocking the rest of the page.

ConcernServer ComponentClient Component
Data fetchingYes - direct, no client cacheOnly for post-interaction data
Secrets and API keysSafe - never shippedNever - all client code is public
useState / useEffectNot availableYes
Event handlersNot availableYes
Heavy dependenciesFree - stays on serverCosts bundle size
Browser APIsNot availableYes
Where each component type belongs

05When it is not worth it

Highly interactive applications - editors, dashboards with live state, design tools, anything resembling a desktop app in the browser - are mostly Client Components by nature. The RSC boundary adds ceremony without much benefit when nearly everything needs interactivity.

The pattern pays off most for content-heavy applications with islands of interaction: marketing sites, documentation, e-commerce, publishing, dashboards that mostly display. If your bundle is dominated by libraries used to render content rather than to handle interaction, RSC will meaningfully shrink it. If your bundle is dominated by genuine interactivity, it will not.

Topics

react server componentsrscnext.js app routerserver side renderingreact performanceclient components

Priya Iyer

Staff Engineer · SyncTrix

Writes about the engineering decisions behind production systems - architecture, delivery and the trade-offs that only show up at scale.

Building something like this?

SyncTrix engineers AI, SaaS, platform and cloud systems for enterprises and high-growth teams. Tell us what you're shipping and we'll scope it with you.