RSC outside Next.js: the framework is the bundler
RSC is React infrastructure, not a Next.js feature: react-server-dom bindings speak Flight, a bundler plugin splits the module graph and emits the client manifest, a client runtime consumes the stream, a router refetches payloads. The discipline transfers everywhere.
The architecture review went sideways on one sentence: “RSC is a Next.js thing, and we are not adopting Next.” The staff engineer pushing back pulled up npm and pointed at react-server-dom-webpack — published by the React team, versioned with React itself, no Next.js anywhere in it. So a tiger team got two sprints to “add RSC to our Vite SPA, it is just React.” Sprint one produced a working Flight stream in isolation. Sprint two produced the education: their bundler had no idea that 'use client' should split the build into two module graphs, nothing emitted the manifest that maps module references in the payload to actual chunk URLs, the dev server crashed because useState does not exist under the react-server condition, and their router happily did full-page loads because nobody taught it to refetch payloads. The Flight protocol was the easy 10%. The team did not fail because RSC is Next-only — it is not — but because the other 90% of an RSC implementation lives in the bundler and the router, which is precisely the part a framework is. Knowing where React ends and the framework begins is what this lesson is for.
By the end of this lesson you will be able to name exactly where React ends and where the framework begins — and explain why dropping a single npm package into an existing SPA is not enough.
RSC is React infrastructure, not a framework feature
The RSC protocol and its reference implementations ship from the React repository itself, as bundler-specific bindings: react-server-dom-webpack, with siblings for other bundlers (Turbopack and Parcel have their own packages; the Vite integration has matured separately as a plugin). The server half exposes functions like renderToPipeableStream — but from react-server-dom-webpack/server, producing a Flight stream, not HTML; the client half exposes createFromFetch / createFromReadableStream, which consume that stream and hand React a renderable tree. None of this mentions Next.js. What Next.js (and every other RSC framework) adds is the machinery around the protocol — and underestimating that machinery is the Hook’s two-sprint lesson.
There is also a second React build involved: packages opt into the react-server export condition, and under that condition react resolves to a build where client-only hooks simply do not exist — useState in a server component fails at module resolution, not at runtime by politeness. Your server bundle and client bundle therefore resolve different code for the same import. This detail alone disqualifies “just run the same bundle on both sides” approaches.
The four parts a minimal RSC setup needs
Strip away every framework and the irreducible checklist is four items:
- A Flight server. Something that takes a request, renders the server component tree under the
react-servercondition, and streams the payload. Tens of lines withreact-server-dom-webpack/server. - A bundler plugin maintaining two module graphs. It must find every
'use client'directive, cut the graph there, compile the client side into chunks, and — the part everyone forgets — emit the client manifest: the table mapping module references in the payload ("./RangePicker.js") to the chunks and export names the browser should load. Get the manifest wrong and hydration dies with “Could not find the module in the React Client Manifest.” - A client runtime. A small bootstrap that fetches the stream, runs it through
createFromFetch, and renders the resulting tree — usually insidestartTransitionso payload swaps do not blow away in-progress input. - A router that refetches payloads. Navigation must intercept link clicks, fetch the new route’s Flight payload instead of HTML, and reconcile it in. Without this you have RSC-rendered first loads and full-page reloads forever after — server components that “re-render on navigation” only if something actually navigates that way.
Items 2 and 4 are where the engineering lives. The bundler work is invasive (two graphs, cross-graph references, manifests kept in sync across dev and prod builds, HMR that understands the cut), which is why RSC support arrives per-bundler and per-framework rather than as one npm install.
Together, these four parts explain the Hook’s failure precisely: the team had item 1 (a Flight stream) but was missing items 2, 3, and 4 — the parts that are not the protocol, but the frame that makes it usable. Without the manifest, references in the payload resolve to nothing; without the router, client components never get their payload updates; without the react-server condition, hooks bleed into the wrong bundle.
A team installs react-server-dom-webpack into their existing Vite SPA, adds 'use client' to a few files, and expects server components to work. What actually blocks them?
Who actually implements it — an honest map
As of early 2026 the landscape, judged by completeness rather than marketing: Next.js App Router is the most complete production implementation and has been the proving ground since 2023 — RSC, server functions, streaming, and caching layered on top (its caching being the most-complained-about part, a Next design, not an RSC requirement). Waku is the minimal reference: a small Vite-based framework built around RSC by design, useful both for production-lite cases and for reading — it is the cleanest way to see the four parts with little else around them. React Router (v7, carrying the Remix lineage) has been landing RSC support as the successor direction of that ecosystem. Parcel ships RSC support with its own react-server-dom bindings, and the Vite RSC plugin effort has been maturing toward making RSC a bundler capability rather than a framework exclusive. The honest summary: one battle-tested implementation, several credible ones converging, and the protocol layer shared by all of them. What you should not expect: dropping RSC into an existing CRA or bare-webpack SPA — CRA is deprecated and its sealed config predates the directive; you would be writing the bundler plugin yourself, which is the job you were trying to avoid.
What transfers, and what the complexity buys
Everything in lessons one and two is framework-portable, because it is protocol-level: server components as the zero-bundle data layer, 'use client' as a module-graph cut, the serialization rules, the DTO discipline at the boundary, the children pattern, server functions as public endpoints. Learn it once; it is the same in Next, Waku, React Router, or anything Flight-based — only file conventions and caching semantics differ.
The tradeoff paragraph your architecture review deserves: RSC charges a real complexity tax — two execution environments to reason about, a serialization boundary that can leak or throw, framework-coupled infrastructure, harder debugging (a stack trace that starts on the server and ends in a hydration mismatch), and team retraining. A classic SPA + REST API keeps one mental model and lets a small team ship; if your app is a dashboard behind a login where the bundle is cached after first visit and SEO is irrelevant, RSC may buy you little. It pays where its structure matches the product: content- and data-heavy pages, bundle-sensitive audiences, large teams that benefit from the enforced data/UI split, and apps already paying for an SSR tier. Adopting half of it — the discipline without the infrastructure — is free, and the discipline is most of the senior-level value anyway.
Hydration fails in a hand-rolled RSC setup with: Could not find the module './Counter.js' in the React Client Manifest. Which part of the stack is broken, and what is it supposed to do?
- 01Name the four parts of a minimal RSC implementation and identify where the real engineering effort concentrates — and why an npm install cannot provide it.
- 02Give the honest adoption picture: who implements RSC today, and how should a small team weigh RSC against a classic SPA plus API?
RSC is infrastructure that ships from the React repository, not a Next.js feature: the react-server-dom bindings implement the Flight protocol — renderToPipeableStream on the server side producing a payload stream, createFromFetch on the client side consuming it — and the react-server export condition gives server bundles a React build where client hooks do not even resolve. A complete implementation is four parts: a Flight server, a bundler plugin that cuts the module graph at every use client directive and emits the client manifest mapping payload references to chunks, a client runtime that turns the stream back into a tree, and a router that refetches payloads on navigation instead of HTML. The bundler and router are the hard 90% — dual module graphs, manifest consistency across dev and prod, HMR that understands the cut — which is why you cannot npm-install RSC into a CRA-era SPA and why support arrives per-bundler, per-framework. The honest 2026 map: Next.js App Router is the battle-tested implementation; Waku is the minimal readable reference; React Router v7 carries the Remix lineage toward RSC; Parcel and the maturing Vite plugin push RSC toward being a bundler capability. What transfers across all of them is everything this unit taught: the two-environment model, the module-graph cut, the serialization rules and DTO discipline, children interleaving, server functions as public endpoints. And the tradeoff stays a tradeoff: RSC taxes you with two environments, a leak-capable boundary and framework coupling — a small team shipping a login-gated dashboard may rationally keep the SPA plus API, while content-heavy, bundle-sensitive, SSR-paying products are where the model earns its complexity. Now when you see a team debating whether to adopt RSC, you can map exactly what they are signing up for: the four parts, where the engineering cost lives, and which half of the trade matches their product.
Practice
Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.
Something unclear?
Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.