필사 모드: React Compiler Got Ported to Rust — What Merged, What Did Not, and That "10x" Number
English- Introduction — The Compiler Was Already 1.0, So Why Port It Again
- What Actually Merged — The Facts First
- '3x' and '10x' — the Warning Label the Author Attached Himself
- "Humans Guided the Architecture, AI Wrote Most of the Code"
- The Real Thing to Look At Is Not Speed, It Is the Verification Method
- Architecture — Same Algorithm, Different Memory Model
- The Month After Merge — Where the Plan Changed, Where It Still Leaks
- Can You Use It Today — npm Has the Answer
- Where the Compiler Still Bites — Memoization Safety
- What Meta's 12% and 2.5x Actually Measured
- Closing — What You Should Do Right Now
- References
Introduction — The Compiler Was Already 1.0, So Why Port It Again
React Compiler looked like a story that had already wrapped up. React Compiler v1.0 shipped on October 7, 2025, and the promise to close out the era of hand-wiring useMemo and useCallback had earned itself a stable-release label.
Then, on June 9, 2026, PR #36173 — [compiler] Port React Compiler to Rust merged into facebook/react main. The entire compiler had been rewritten from TypeScript into Rust: 461 files changed, 123,289 lines added. The author is Joseph Savona (@josephsavona) of the React team.
Within days, the sentence "React Compiler is 10x faster in Rust" started making the rounds. This post traces that sentence back to primary sources. The short version: the merge is a fact; the number is hard to call a fact — because the author himself wrote as much.
What Actually Merged — The Facts First
The first sentence of the PR body says it all.
This is an experimental, work-in-progress port of React Compiler to Rust.
The items that follow keep the same tone.
- Work in progress. "we are sharing early, prior to testing internally at Meta, to get feedback from partners in parallel with continued development" — meaning this went public before internal testing at Meta even started, specifically to get partner feedback.
- No builds. "No builds available yet, you'll have to do some hacking if you want to try this." If you want to try it, you are on your own.
- No known gaps, but lurking bugs are possible. "All fixtures pass, no known gaps but there may be lurking bugs."
The timeline: opened March 30, 2026, merged June 9. Two and a half months.
One more thing worth flagging: this is not the first attempt. The repository history shows a commit from January 24, 2025 titled [compiler][be] Remove unused experimental Rust port. A Rust port was tried once before, went unused, and was deleted. This is round two.
'3x' and '10x' — the Warning Label the Author Attached Himself
Now for the number in question. Here is the performance item from the PR body, in full.
Early performance numbers are derived from AI and i haven't spent much time validating the benchmark setup, beyond the fact that the optimization opportunities it discovered made complete sense and the fixes were right. With that caveat, it does appear that the Rust version is quite fast already: 3x faster when operating as a Babel plugin. The serialization cost is quite high, but the actual transformation logic is ~10x faster, so it's net faster. Native integrations (oxc, swc) should be even faster.
Let us pick apart exactly what is happening here.
First, the number comes from AI. The author did not write and run a benchmark himself; the figure came from AI, and he states plainly that he "haven't spent much time validating the benchmark setup." What he says he did verify is not the performance number but that the optimization opportunities the AI found made sense. That is a completely different claim.
Second, 3x and 10x are not measuring the same thing. 3x is the end-to-end number when running as a Babel plugin; 10x is transformation logic only, with the serialization cost stripped out. The moment you round it up to "10x faster," the serialization cost quietly disappears — and the author says, in the very same sentence, that this cost is "quite high." The number that actually reaches your build is the 3x one, not the 10x one.
Third, "oxc/swc are faster" is a prediction, not a measurement. The original says "should be even faster" — note the modal verb. It should not be rendered as a flat "are faster."
Fourth, even that 3x is already stale. There is some irony here, which we will get to below.
To sum up, these are numbers that were produced by AI on an experimental WIP branch, and that the author himself says he did not validate. That does not mean you cannot cite them — it means you have to carry the conditions along whenever you do. This ecosystem has a real talent for turning unconditioned numbers into folklore.
"Humans Guided the Architecture, AI Wrote Most of the Code"
This is the other line from the PR that got quoted almost as much as the number.
The architecture was heavily guided by humans (me, @josephsavona) but majority coded by AI.
Lift out just "majority coded by AI" and it is easy to misread. The very next sentence narrows the scope.
I was very hands-on in setting the architecture, the testing and verification strategy, incremental migration approach, etc. I also kept a close eye on the code and spent a decent amount of time going back and forth to get code quality to a decent level.
In other words, what the human set was the architecture, the testing and verification strategy, and the incremental migration approach, and code quality was also pulled up by "going back and forth" repeatedly. What AI filled in was the implementation inside that frame.
It is also worth noting that the conditions here favored AI. This was not designing a compiler from a blank page — it is a port with the answer key already in hand. The TypeScript implementation exists as a reference, there is a 1:1 correspondence pass by pass (in the author's words, "It's very much a pass-by-pass port"), and the output can be checked mechanically for correctness. This was a problem where the spec already existed in an executable form. Generalizing the result here into "AI can build a compiler" overreaches.
The Real Thing to Look At Is Not Speed, It Is the Verification Method
Personally, the most instructive part of this PR is not the 10x — it is this: how do you build trust when you hand AI a port that already has an answer key?
Step 1 — output comparison.
all 1725 fixtures pass in snap when comparing the temporary rust version of the plugin with the main version. this compares generated code output as well as errors.
Across 1,725 fixtures, the Rust version is compared against the existing version on generated code output and errors. So far, this is a familiar golden-file test.
Step 2 — per-pass internal-state comparison. This is the crux of it.
all fixtures also pass a full comparison of the per-pass compiler intermediate representation — the intermediate state (including log events and errors) are ~identical after every single pass (modulo some normalization of ids)
Matching only the final output lets "the same answer by accident" pass. So instead, the entire intermediate representation (IR), including log events and errors, is compared after every single pass. The script the author built for this is compiler/script/test-rust-port.sh, and per the PR description, it can be pointed at any arbitrary JS directory, which is planned for use in Meta's internal testing.
The lesson boils down to this: when you hand AI a large-scale port, what you need to build is not the code but the comparison rig. A machine has to be able to confirm not "the output matches" but "every intermediate step matches" before you can trust 120,000 lines without a human reading all of it. It is not a coincidence that "testing and verification strategy" is on the list of things the author says he was "hands-on" about.
There is an honest gap here too, though.
The OXC and SWC example integrations seem to be working well, though i haven't manually verified this to the same extent as i have the Babel integration.
He says he has not manually verified these to the same degree as the Babel integration.
Architecture — Same Algorithm, Different Memory Model
The internal structure is surprisingly conservative. Per the PR description itself, the Rust version has the same architecture as the TypeScript version.
AST -> HIR (High-level Intermediate Representation)
- CFG (control-flow graph)
- SSA (single-static assignment)
|
+-> same passes, same algorithms
|
v
output AST
Open compiler/crates in the repository and this structure is split straight into crates — react_compiler_hir, react_compiler_ssa, react_compiler_lowering, react_compiler_optimization, react_compiler_reactive_scopes, react_compiler_typeinference, react_compiler_validation, and so on.
What changed is not the algorithm but the data representation.
The main differences are in the data representation - using arena-like structures (and indices into these arenas) to work within Rust's borrowing system.
A graph that TypeScript tied together with object references cannot be carried over to Rust as-is — the borrow checker does not allow cyclic references and mutable aliasing. So nodes get packed into an arena and referenced by index instead. This has become close to the standard pattern for porting a structure where nodes point at each other, like CFG and SSA, into Rust.
The public API is unusual too.
The public API is basically "Rust Babel AST" + Scope Info in, Rust Babel AST out.
The Rust representation of the Babel AST is used as the shared interface, and each integration (Babel, OXC, SWC) converts to and from its own representation at the boundary. The reason is practical — "the conversion from any AST into our HIR is complex, and we can only maintain one version." On top of that, right now the integration side has to serialize and hand over scope information itself, because the TypeScript version rode on top of Babel's scope analysis. Implementing its own scope resolution is stated as a planned item.
This explains the earlier "the serialization cost is quite high." The round trip of handing the Babel AST to Rust and back, plus serializing the scope graph on top of it, is what was eating the transformation logic's 10x gain down to 3x.
The Month After Merge — Where the Plan Changed, Where It Still Leaks
From here on is what you miss if you only read the PR body. The commit history is far more honest.
Work on the serialization cost started immediately. Two days after merge, on June 11, [rust-compiler] Return the compiled AST by value instead of JSON (#36729) and [rust-compiler] Carry uninspected AST subtrees as raw JSON text (#36730) landed. The compiled AST now comes back as a value instead of JSON, and AST subtrees that do not need inspecting are just carried around as raw JSON text. This is what "the 3x is already stale" meant earlier — the code that produced that number started changing right after merge. Nobody has published what the multiplier is now.
The OXC/SWC plan got reversed. The PR body sketched a picture where "our repo has one crate per integration owner (react_compiler_swc, react_compiler_oxc), and most of the logic lives there." But on June 11, [compiler] Remove OXC and SWC plugins in favor of them being handled by those projects (#36743) merged. The reason given is one line — "I believe the consensus is that the OXC and SWC plugins should live in those projects, and instead consume the React compiler as a crate." And indeed, compiler/crates has no oxc/swc crate today. The integration strategy changed two days after merge, and reading only the PR body leaves you with the wrong picture.
New problems showed up specifically because of the move to Rust. On June 16, [rust-compiler] Represent string values as JsString (WTF-16 aware) (#36731). JavaScript strings are WTF-16, and Rust's String is UTF-8. If you just take a string with unpaired surrogates and drop it into a Rust string, you cannot preserve the original losslessly. A category of bug that did not exist in TypeScript came along for the ride with the language change. On June 22, a typical Rust performance tune-up landed too — Use rustc-hash FxHasher for all maps and sets (#36811).
"All fixtures pass" needs a footnote. compiler/crates/TODO.md, which landed with the merge commit, has an e2e-parity snapshot table.
Variant | Score | Failures
Babel | 1792 / 1802 | 10
SWC | 1786 / 1802 | 16
OXC | 1704 / 1795 | 91
This does not contradict the PR body's "all 1,725 fixtures pass" — it is a different suite. The earlier one is the snap suite (Rust plugin output compared against the existing plugin's output); this table is the e2e-parity suite that pits the three integration variants against the TypeScript implementation. The point is that, broken down by integration, it does not all pass yet, and the document itself notes "the OXC row predates the fixtures and has not been re-measured." For reference, cargo test --workspace passes 84 and fails 0. This file has not been touched since the June 9 merge, so the current numbers may differ.
And gaps are still being picked up. On July 8, [compiler] Bail out with Todo on using and await using declarations (#36946) — it now bails out when it hits a using / await using declaration. Commits that port fixes from the TypeScript side over to Rust keep coming in too, like [compiler] Port JSX tag classification fix to Rust (not-lowercase is a component) (#36951) on July 7. This is where the real cost of this port shows up — for the time being, two implementations have to be maintained in parallel.
Can You Use It Today — npm Has the Answer
This question is answered faster with a command than with a search.
npm view babel-plugin-react-compiler dist-tags
# latest: 1.0.0
# experimental: 0.0.0-experimental-a1856f3-20260507
As of July 16, 2026, latest is still 1.0.0 (from October 2025), and the last publish was an experimental build from May 8, 2026. In other words, there is no npm build yet that contains the Rust port merged on June 9. The PR's "No builds available yet" still holds today.
There is a cross-check too. OXC's React Compiler documentation marks this feature "experimental and under active development," warns that "Options and behaviour may change," and states that it vendors the Rust port — merged into React but still unpublished — as a releasable crate at oxc-project/forked-react-compiler. Since upstream has not published it, they are running off a fork.
And there is no post about this port on the react.dev blog. The most recent post right now is The React Foundation from February 24, 2026. This is a change that merged quietly, with no official announcement.
Right here you can watch folklore get manufactured in real time. Search around and you will find sentences like "Next.js 16.4 shipped with the Rust compiler and builds got more than 40% faster." Checking it looks like this.
npm view next dist-tags
# latest: 16.2.10
# preview: 16.3.0-preview.6
Next.js 16.4 does not exist. The 40% number has no source either. A nonexistent benchmark for a nonexistent version is already circulating in the shape of a quotable sentence. When you are looking at framework performance numbers, one line of npm view beats ten search results.
Where the Compiler Still Bites — Memoization Safety
Rust versus TypeScript does not change where the compiler bites you. It is a port of the same algorithm. So this is the part that matters more for practitioners.
React Compiler knows about APIs where automatic memoization is not safe, and it excludes those modules from optimization. That list lives in DefaultModuleTypeProvider, and as of July 2026 it has exactly three entries.
react-hook-form— onlywatch()among whatuseForm()returns@tanstack/react-table@tanstack/react-virtual
The source comment explains the reason well. The problem is interior mutability. An API that mutates a value in place instead of creating a new value and putting it into state can produce a different call result even while the function object stays the same. React memoizes on the assumption that "if the inside changes, the outside object changes too," and this assumption breaks. The comment sums up the situation this way: libraries built before the Rules of React were precisely documented shipped memoization-unsafe APIs by accident, and if a developer manually memoizes them, the app breaks — or they try the compiler and conclude "the compiler broke my code."
An incident ten days ago shows this is not an abstract worry. On July 6, [compiler]: add useWindowVirtualizer to known incompatible libraries. Back in September 2025, @tanstack/react-virtual's useVirtualizer had been added to the list, but useWindowVirtualizer was missed. The two are thin wrappers around the same internal implementation, so they share the same problem. The outcome was concrete — in the reporter's app, getVirtualItems() froze at its first-render value (an empty array, before any measurement), leaving the virtualized list permanently empty. It had been silently compiled with incorrect memoization.
There are two things to take from this.
First, this safety net is a hardcoded list. It only blocks known modules. A library (or your own in-house hook) that uses the same pattern but is not on the list sails straight through. The OXC documentation says the same thing — "Code that breaks the Rules of React is also skipped rather than optimized — for example interior mutability, or libraries built on observable mutation such as MobX's observer()." MobX's observer() is given as an example.
Second, the list now has to be fixed twice. #36912 put the fix in both DefaultModuleTypeProvider.ts and the Rust crate's default_module_type_provider.rs. This is exactly what the dual-maintenance cost mentioned earlier looks like in practice.
As an aside, the OXC documentation also notes an integration-ordering trap — "The React Compiler requires the original source: it must see JSX before any other transform." If some other plugin touches JSX first — emotion's CSS prop, or Babel's constant-elements, for instance — it breaks.
What Meta's 12% and 2.5x Actually Measured
There is another number that often gets mixed up with the Rust story, so let us separate it out. This is a line from the React Compiler v1.0 announcement.
We've seen initial loads and cross-page navigations improve by up to 12%, while certain interactions are more than 2.5× faster.
Attach the conditions precisely and it reads like this.
- Who: This is a figure reported by the React team (Meta) from their own app. It is not an independent benchmark.
- What: initial loads and cross-page navigations improved by up to 12%, and certain interactions by more than 2.5x. "up to" and "certain" are not decoration — they are scope qualifiers. This is not an average.
- When: October 2025, on TypeScript version 1.0. It has nothing to do with the Rust port.
- It also adds that memory usage was neutral.
And the announcement itself says, "Although your mileage may vary, we recommend experimenting with the compiler in your app to see similar performance gains." It is also easy to blur the fact that 12% is runtime performance while 3x/10x is build-time compilation speed — these numbers sit on different axes. Mix them together into a sentence like "the compiler got 10x faster so the app got 12% faster" and you are wrong twice over.
Closing — What You Should Do Right Now
To sum up.
What is true: The Rust port of React Compiler merged on June 9, 2026. The architecture is identical to the TypeScript version (HIR + CFG + SSA, ported pass by pass); the difference is mainly the arena-and-index-based data representation. Humans set the architecture and verification strategy while AI wrote most of the implementation, and it comes with a fairly solid verification mechanism — per-pass IR comparison.
What is hard to call a fact: "10x." This is a number that AI produced and that the author states he did not validate the benchmark setup for; the end-to-end figure is really 3x, and even the code behind that 3x is already stale, since the serialization path started changing right after merge. Claims like "Next.js 16.4's 40%" are about a version that does not even exist.
What changes for you: nothing, as of today. npm's latest is 1.0.0 and the Rust build is not published. If you are using React Compiler right now, you are using TypeScript 1.0, and that is exactly as it should be. If there is any preparation worth doing, it is what the v1.0 announcement already recommends — pin to an exact version (1.0.0, not ^1.0.0) and have e2e tests in place. The reasoning behind that recommendation is that how memoization gets applied can vary between versions, and that is even more true during a transition where the implementation is being swapped out wholesale.
When to check back: npm view babel-plugin-react-compiler dist-tags will tell you. The moment to watch for is when the Rust build gets published, an official post goes up on react.dev, and the parity table in crates/TODO.md gets cleaned up. Until then, the repository — not a benchmark tweet — is what tells you the actual state.
Last, the lesson from this whole episode that seems likely to outlast the performance numbers is a methodological one, not a speed one. What built trust while porting a 120,000-line compiler with AI was not code review — it was a rig that mechanically compares the intermediate representation at every pass. It is worth remembering, alongside that, the limitation that this was only possible because the problem came with an answer key. And the fact that even the person who wrote the PR footnoted his own numbers with "I have not validated this," while the ecosystem quoted the number and dropped the footnote — that may be the lesson that keeps repeating longest.
References
- PR #36173 —
[compiler] Port React Compiler to Rust(josephsavona, opened 2026-03-30 / merged 2026-06-09) - PR #36743 — removes the OXC/SWC plugins so those projects consume it as a crate instead
- PR #36912 — adds
useWindowVirtualizerto the known-incompatible-libraries list - PR #34493 — adds
@tanstack/react-virtualto the known-incompatible-libraries list (2025-09) compiler/crates— the Rust crate layout and theTODO.mdparity snapshotDefaultModuleTypeProvider.ts— interior mutability and the incompatible-library list- React Compiler v1.0 (react.dev, 2025-10-07) — Quest Store 12% / 2.5x, version-pinning recommendation
- React blog — most recent post is 2026-02-24, The React Foundation (no official announcement of the Rust port)
- OXC — React Compiler integration docs (experimental, vendors the unpublished port)
- oxc-project/forked-react-compiler — the crate OXC vendors
- Next.js 16 Architecture — A Deep Dive into Turbopack and Cache Components (related post)
현재 단락 (1/98)
React Compiler looked like a story that had already wrapped up. [React Compiler v1.0](https://react....