Bun PR #32504, merged on June 20, 2026, turns the upstream React Compiler Rust port into a built-in bun build transform. Turn it on with --react-compiler from the CLI or reactCompiler: true on Bun.build, and Bun will memoize your .jsx and .tsx components and hooks during the build, with no Babel plugin, no config files, and nothing to install. The feature is off by default and marked experimental in both the type definitions and the bundler docs.
This is the first bundler to ship the React Compiler as a native transform. Vite, Next.js with Turbopack, webpack, and Rsbuild all run it through a Babel or SWC plugin today. Bun's path skips that intermediate entirely.
What landed
The integration closes issue #24356, the long-standing feature request for first-class React Compiler support in the bundler, and replaces an earlier PR #31785 that depended on an oxc_react_compiler crate that did not exist at the time. The new PR ports the compiler's Rust workspace directly from facebook/react rather than going through Oxc, which is why the upstream PR description in facebook/react#36173 explicitly invited bundler integrations via the react_compiler_oxc adapter and Bun took a different path.
A follow-up PR #32545 shipped the same day fixes three review comments from the merged PR, including a subtle bug where reactCompilerOutputMode: 'client' would silently enable the compiler even when reactCompiler: false was set. The output mode is now stored separately and only applied when the compiler is on, matching the documented behavior in bun.d.ts.
The architecture: Bun AST straight to HIR
The compiler lives in src/react_compiler/, a single ~62k LOC crate. The bulk of it is a byte-for-byte port of the upstream Rust workspace, with import paths rewritten and the serde and serde_json derives that Bun does not need stripped. The upstream workspace crates that are ported whole: hir/, ssa/, inference/, typeinference/, optimization/, validation/, reactive_scopes/, diagnostics/, and utils/. Hot-path data structures were densified: HashMap<SmallId, _> becomes Vec<_>, HashSet<ValueReason> becomes EnumSet (u16), and points-to sets become SmallVec<[_; 4]>. The IndexMap and IndexSet API is shimmed over arena-backed bun_collections::ArrayHashMap.
The four layers that touch the AST (lowering, codegen, pipeline, and the program/imports glue) are reimplemented against bun_ast, with the type-mapping table in src/react_compiler/DESIGN.md documenting how Bun's AST nodes correspond to the Babel-shaped AST the compiler expects.
The compile hook fires inside visit_stmts(FnBody), between its visit phase and its inline-mangle phase. Candidate detection on S::Function, S::Local, S::ExportDefault, and S::Expr records the binding Ref and the memo/forwardRef wrapper bit; visit_func and arrow-visit copy the function's args, flags, and locations into a Copy PendingCompile struct; the hook calls maybe_compile_pending, which constructs a stack-local G::Fn and runs maybe_compile_node. The compiled body lands in the live stmts buffer so the existing mangle phase runs on it. New arguments and flags flow back through a single CompileResult field. No raw pointers, no extra pass; the non-RC path adds one is_some() check per top-level declaration.
The compiler also honors // eslint-disable react-hooks/* suppressions. The lexer runs one substring check per comment, gated on the feature flag, and propagates the suppression as a flag bit on G::Fn and E::Arrow; the compiler skips any function carrying it.
The numbers
The PR ships a benchmark on a large React codebase (around 860 compiled components, 1400 memo slots). The same code, on the same machine:
| Wall time | vs Babel plugin | |
|---|---|---|
Baseline (reactCompiler: false) | 394 ms | - |
reactCompiler: true | 465 ms (1.18x baseline) | ~20x faster than Babel |
| Babel plugin (same input) | 9.15 s | 1x |
The full --compile standalone executable build, which bundles everything plus the React Compiler pass, runs in 3.62 s with the Rust port versus 13.04 s with the Babel plugin, a 3.6x end-to-end speedup.
These are not synthetic micro-benchmarks. The codebase is real, the components compile to real _c(N) memoization calls with $[0] !== label cache checks, and the react/compiler-runtime import Bun injects resolves against the React 19+ install that ships with the app. Bun notes that the baseline-with-RC overhead (394 ms to 465 ms, ~18%) is from HIR construction and SSA pass; the rest of the bundler (parser, mangle, minify) is unchanged.
What the API looks like
CLI:
bun build ./app.tsx --react-compiler --target browser
Bun.build:
await Bun.build({
entrypoints: ["./app.tsx"],
reactCompiler: true,
// reactCompilerOutputMode: "client", // default for browser target
// reactCompilerOutputMode: "ssr", // default for bun/node target
target: "browser",
});
reactCompilerOutputMode defaults to "client" when target is "browser" and to "ssr" when target is "bun" or "node". SSR mode skips the useMemoCache runtime so server-rendered output stays cache-friendly across requests. compilationMode: "infer" semantics carry over from the upstream compiler, so only components and hooks are compiled; "use no memo" directives are honored, and node_modules is skipped.
What this means for the bundler race
This is the first time the Rust port of the React Compiler has shipped as a build-time transform rather than as a library other tools have to plug into. The Oxc v0.135 integration in mid-June added the compiler as a Rust crate you could call into, but the only bundler to actually wire it up since is Bun. Vite 8 and Vite 8.1 still go through babel-plugin-react-compiler; Next.js with Turbopack uses the SWC port; webpack uses Babel. Bun's choice to port upstream directly into its own AST layer is a deliberate trade: it skips the cross-AST conversion cost and the dependency surface, at the price of having to re-sync against facebook/react periodically.
The maintenance path is wired up. scripts/sync-react-compiler.sh sparse-fetches facebook/react and prints a per-file diff between src/react_compiler/UPSTREAM_PORTED and upstream tip, grouped into whole-crate ports that apply mechanically and AST-boundary ports that re-port via the type-mapping table. --fixtures re-syncs the test corpus. As long as the upstream API stays Babel-AST-shaped, the cost of tracking the port is roughly proportional to how often upstream touches the boundary layers.
Where to watch
Three signals worth tracking over the next few weeks:
- The Bun v1.3.15 release notes when they land, which should bundle PR #32504 plus the follow-up and promote the feature from
bun buildexperimental to a stable flag. - The Oxc
react_compiler_oxcadapter landing as a stable crate in an Oxc release, which is the path Vite and Rolldown will most likely take to get the same perf numbers without porting upstream. - Any change in the upstream React Compiler's "public API" from "Babel AST + scope info" to a more bundler-native shape, which would let Oxc (and through it Vite, Next.js, Rsbuild) skip their own adapter crates entirely.



