惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
C
CERT Recently Published Vulnerability Notes
C
Cisco Blogs
Cloudbric
Cloudbric
The Last Watchdog
The Last Watchdog
L
LINUX DO - 热门话题
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Application and Cybersecurity Blog
Application and Cybersecurity Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Security Archives - TechRepublic
Security Archives - TechRepublic
TaoSecurity Blog
TaoSecurity Blog
V2EX - 技术
V2EX - 技术
H
Heimdal Security Blog
S
Security Affairs
L
Lohrmann on Cybersecurity
Hacker News - Newest:
Hacker News - Newest: "LLM"
Simon Willison's Weblog
Simon Willison's Weblog
WordPress大学
WordPress大学
小众软件
小众软件
Security Latest
Security Latest
AWS News Blog
AWS News Blog
Apple Machine Learning Research
Apple Machine Learning Research
GbyAI
GbyAI
Engineering at Meta
Engineering at Meta
阮一峰的网络日志
阮一峰的网络日志
罗磊的独立博客
F
Full Disclosure
S
Schneier on Security
L
LangChain Blog
MyScale Blog
MyScale Blog
Know Your Adversary
Know Your Adversary
P
Privacy International News Feed
Google Online Security Blog
Google Online Security Blog
Scott Helme
Scott Helme
Stack Overflow Blog
Stack Overflow Blog
爱范儿
爱范儿
A
Arctic Wolf
Martin Fowler
Martin Fowler
B
Blog RSS Feed
大猫的无限游戏
大猫的无限游戏
博客园 - 三生石上(FineUI控件)
The Register - Security
The Register - Security
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
博客园_首页
Latest news
Latest news
F
Fortinet All Blogs
G
GRAHAM CLULEY
T
The Exploit Database - CXSecurity.com
Hacker News: Ask HN
Hacker News: Ask HN

overreacted — A blog by Dan Abramov

There Are No Instances in atproto — overreacted Algebraic Effects for the Rest of Us — overreacted A Social Filesystem Hire Me in Japan — overreacted How to Fix Any Bug — overreacted Where It's at:// — overreacted Open Social A Lean Syntax Primer — overreacted Beyond Booleans — overreacted The Math Is Haunted — overreacted Suppressions of Suppressions — overreacted I'm Doing a Little Consulting — overreacted How Imports Work in RSC — overreacted RSC for LISP Developers — overreacted Progressive JSON — overreacted Why Does RSC Integrate with a Bundler? — overreacted One Roundtrip Per Navigation — overreacted Static as a Server — overreacted RSC for Astro Developers — overreacted Functional HTML — overreacted What Does "use client" Do? — overreacted Impossible Components JSX Over The Wire React for Two Computers The Two Reacts — overreacted A Chain Reaction — overreacted npm audit: Broken by Design — overreacted Before You memo() — overreacted The WET Codebase — overreacted Goodbye, Clean Code — overreacted My Decade in Review — overreacted What Are the React Team Principles? — overreacted On let vs const — overreacted What Is JavaScript Made Of? — overreacted How Does the Development Mode Work? — overreacted Preparing for a Tech Talk, Part 3: Content — overreacted Name It, and They Will Come — overreacted Writing Resilient Components — overreacted A Complete Guide to useEffect How Are Function Components Different from Classes? — overreacted Coping with Feedback — overreacted Fix Like No One’s Watching — overreacted Making setInterval Declarative with React Hooks — overreacted React as a UI Runtime Why Isn’t X a Hook? — overreacted The “Bug-O” Notation — overreacted Preparing for a Tech Talk, Part 2: What, Why, and How — overreacted The Elements of UI Engineering — overreacted Things I Don’t Know as of 2018 — overreacted Preparing for a Tech Talk, Part 1: Motivation — overreacted Why Do React Hooks Rely on Call Order? — overreacted Optimized for Change — overreacted How Does setState Know What to Do? — overreacted My Wishlist for Hot Reloading — overreacted Why Do React Elements Have a $$typeof Property? — overreacted How Does React Tell a Class from a Function? — overreacted Why Do We Write super(props)? — overreacted
Introducing RSC Explorer — overreacted
2025-12-19 · via overreacted — A blog by Dan Abramov

In the past few weeks, since the disclosure of the critical security vulnerability in React Server Components (RSC), there’s been a lot of interest in the RSC protocol.

The RSC protocol is the format in which React trees (and a superset of JSON) get serialized and deserialized by React. React provides both a writer and a reader for the RSC protocol, which are versioned and evolved in lockstep with each other.

Because the RSC protocol is an implementation detail of React, it is not explicitly documented outside the source code. The benefit of this approach is that React has a lot of leeway to improve the format and add new features and optimizations to it.

However, the downside is that even people who actively build apps with React Server Components often don’t have an intuition for how it works under the hood.

A few months ago, I wrote Progressive JSON to explain some of the ideas used by the RSC protocol. While you don’t “need” to know them to use RSC, I think it’s one of the cases where looking under the hood is actually quite fun and instructive.

I wish the circumstances around the increased interest now were different, but in any case, that interest inspired me to make a new little tool to show how it works.

I’m calling it RSC Explorer, and you can find it at https://rscexplorer.dev/.

Obviously, it’s open source.


“Show, don’t tell”, as they say. Well, there it is as an embed.

Let’s start with the Hello World:

Notice there’s a yellow highlighted line that says something cryptic. If you look closely, it’s <h1>Hello</h1> represented as a piece of JSON. This line is a part of the RSC stream from the server. That’s how React talks to itself over the network.

Now press the big yellow “step” button!

Notice how <h1>Hello</h1> now appears on the right. This is the JSX that the client reconstructs after reading this line. We’ve just seen a simple piece of JSX—the <h1>Hello</h1> tag—cross the network and get revived on the other side.

Well, not really “cross the network”.

One cool thing about RSC Explorer is that it’s a single-page app, i.e. it runs entirely in your browser (more precisely, the Server part runs in a worker). This is why, if you check the Network tab, you’ll see no requests. So in a sense it’s a simulation.

Nevertheless, RSC Explorer is built using exactly the same packages that React provides to read and write the RSC protocol, so every line of the output is real.


Async Component

Let’s try something slightly more interesting to see streaming in action.

Take this example and press the big yellow “step” button exactly two times:

(If you miscounted, press “restart” on the left, and then “step” two times again.)

Have a look at the upper right pane. You can see three chunks in the RSC protocol format (which, again, you don’t technically need to read—and which changes between versions). On the right, you see what Client React reconstructed so far.

Notice a “hole” in the middle of the streamed tree, visualized as a “Pending” pill.

By default, React would not show an inconsistent UI with “holes”. However, since you’ve declared a loading state with <Suspense>, a partially completed UI now can be displayed (notice how the <h1> is already visible but <Suspense> shows the fallback content because <SlowComponent /> has not streamed in yet).

Press the “step” button once again, and the “hole” will be filled.


Counter

So far, we’ve only sent data to the client; now let’s also send some code.

Let’s use a counter as the classic example.

Press the big yellow “step” button twice:

That’s just a good old counter, nothing too interesting here.

Or is there?

Have a look at the protocol payload. It’s a bit tricky to read, but notice that we’re not sending the string "Count: 0" or the <button>s, or any HTML. We’re sending <Counter initialCount={0} /> itself—the “virtual DOM”. It can, of course, be turned to HTML later, just like any JSX can, but it doesn’t have to be.

It’s like we’re returning React trees from API routes.

Notice how the Counter reference becomes ["client",[],"Counter"] in the RSC protocol, which says “grab the Counter export from the client module”. In a real framework, this would be done by the bundler, which is why RSC integrates with bundlers. If you’re familiar with webpack, this is similar to reading from the webpack require cache. (In fact, that’s how RSC Explorer implements that.)


Form Action

We’ve just seen the server referring to a piece of code exposed by the client.

Now let’s see the client referring to a piece of code exposed by the server.

Here, greet is a Server Action, exposed with 'use server' as an endpoint. It’s passed as a prop to the Client Form component that sees it as an async function.

Press the big yellow “step” button three times:

Now enter your name in the Preview pane and press “Greet”. The RSC Explorer debugger will “pause” again, showing we’ve hit the greet Server Action with a request. Press the yellow “step” button to see the response returned to the client.


Router Refresh

RSC is often taught with a framework, but that obscures what’s happening. For example, how does a framework refresh server content? How does a router work?

RSC Explorer shows frameworkless RSC. There’s no router.refresh—but you can implement your own refresh Server Action and a Router Component.

Press the “step” button repeatedly to get the whole initial UI on the screen:

Look at the ticking timer. Notice how the ColorTimer component from the Server passed a random color to the Timer component on the Client. Again, the Server has returned <Timer color="hsl(96, 70%, 85%)" /> (or such).

Now press the Refresh button right above the timer.

Without digging into the code, “step” through the server response and see what happens. You should see a continously ticking Timer receive new props from the server. Its background color will change but its state will be preserved!

In a sense, it’s like refetching HTML using something like htmx, except it’s a normal React “virtual DOM” update, so it doesn’t destroy state. It’s just receiving new props… from the server. Press “Refresh” a few times and step through it.

If you want to look how this works under the hood, scroll down both Server and Client parts. In short, the Client Router keeps a Promise to the server JSX, which is returned by renderPage(). Initially, renderPage() is called on the Server (for the first render output), and later, it is called from the Client (for refetches).

This technique, combined with URL matching and nesting, is pretty much how RSC frameworks handle routing. I think that’s a pretty cool example!


What Else?

I’ve made a few more examples for the curious folks:

And, of course, the infamous:

(As you would expect, this one only works on the vulnerable versions so you’d need to select 19.2.0 in the top right corner to actually get it to work.)

I’d love to see more cool RSC examples created by the community.

RSC Explorer lets you embed snippets on other pages (as I’ve done in this post) and create sharable links as long as the code itself is not bigger than the URL limit. The tool is entirely client-side and I intend to keep it that way for simplicity.

You’re more than welcome to browse its source code on Tangled or GitHub. This is a hobby project so I don’t promise anything specific but I hope it’s useful.

Thank you for checking it out!