Peeking Inside React Server Components

The recent disclosure of a critical security vulnerability in React Server Components (RSC) brought a lot of attention to the underlying protocol. RSC is the serialization format React uses to move component trees between server and client. React ships both the writer and reader for this protocol, evolving them in lockstep. It's an implementation detail, not a documented API, which gives the React team room to optimize, but it also means many developers building with RSC lack an intuition for what's happening on the wire.

To bridge that gap, a new tool called RSC Explorer is now available at https://rscexplorer.dev/. It's an open-source, single-page app that lets you step through real RSC protocol payloads and watch how the client reconstructs the server-rendered tree. The server part runs in a browser worker, so no network requests are made—it's a simulation, but it uses the exact same React packages for reading and writing the protocol, so every line of output is genuine.

Seeing the Stream, Line by Line

The simplest example is a Hello World component. In the initial view, you see a yellow-highlighted line containing a piece of JSON that encodes <h1>Hello</h1>. That's the full RSC stream from the server. Pressing the "step" button reveals the client-side reconstruction: the JSX for that <h1> tag appears in the preview pane after being revived from the stream.

The more interesting case involves an async component. Stepping through this example reveals the mechanics of streaming. The upper-right pane shows the RSC protocol chunks that have arrived so far; the right pane shows what the client has reconstructed up to that point. With an unresolved <Suspense> boundary, there's a visible "hole" in the middle of the tree, displayed as a "Pending" pill. React wouldn't normally render an inconsistent UI, but with a loading state declared, the client shows the fallback content until the slow component streams in. One more step fills the hole.

Sending Code, Not Just Data

The protocol also handles client components. In a counter example, stepping through the payload shows that the server isn't sending HTML or the rendered output like "Count: 0" or the <button> elements. Instead, it sends the component reference itself: <Counter initialCount={0} />—the virtual DOM representation. In the stream, this becomes ["client",[],"Counter"], instructing the client to grab the Counter export from the specified module. In a real framework, a bundler handles this mapping; RSC Explorer uses a webpack shim to do the same thing.

The reverse direction also works. Server Actions, exposed with 'use server', appear to the client as async functions passed as props. In the Form Action example, you can type a name into the preview, press "Greet", and the debugger pauses to show the action invocation. Stepping through reveals the response sent back to the client.

State Preservation and Routing

RSC Explorer demonstrates a frameworkless approach, showing how a router and refresh mechanism can be built from primitives. A ColorTimer server component passes a random background color to a client-side Timer component, which is ticking and maintaining its own state. Pressing the Refresh button above the timer returns new props from the server—a new random color—while preserving the timer's state through the update.

This works because the client-side Router keeps a reference to the server JSX returned by renderPage(). The function is called initially on the server for the first render, and later from the client to trigger a refetch. Combined with URL matching and nesting, this pattern is essentially how RSC frameworks implement routing.

More Examples and Embedding

Additional examples are available for pagination, error handling, client references, bound actions, binary data, and a kitchen sink demonstration. There's also a CVE-2025-55182 example that recreates the vulnerability—it only works if you select version 19.2.0 in the top-right corner.

RSC Explorer is entirely client-side, supports embedding snippets on other pages, and allows shared links as long as the code fits within the URL limit. The source is available on GitHub and Tangled.