If you run @vitejs/plugin-react v6.1.0, the answer is to enable the native React Compiler and delete the Babel plugin chain: one production migration took a 1,036-file build’s compiler stage from 14.3s to 0.81s1 while compiling more JavaScript than the Babel v1.0 path does. Read that with its stated scope, though: one codebase (Outlyne, a website builder), one workload, a vendor-adjacent benchmark, and an integration that ships labeled experimental. The decision underneath is durable anyway. Memoization stops being developer discipline and becomes build-tool policy.
What shipped in August 2026?
The oxc team shipped official support for a Rust-written React Compiler on August 4, 2026, and @vitejs/plugin-react v6.1.0 followed with what the release describes as “experimental native React Compiler support,” enabled by passing { compiler: true } to the plugin.
The compiler itself is not new (see React’s compiler documentation). The job it does by rewriting your components is the job developers otherwise do by hand with useMemo and useCallback, the manual half of React’s performance-optimization hook family alongside useTransition, useDeferredValue, and useSyncExternalStore. What changed in August is distribution. Until now the practical path was a Babel plugin: you bolted a Babel transform stage onto your Vite build to run the compiler, priced every compile in Babel’s transform cost, and maintained the wiring yourself. A native option inside the official plugin turns that into a config flag, which is the difference between an experiment and a default-in-waiting.
The delivery route explains the shape of the integration. React is a library, not a framework; react.dev explicitly recommends full-stack frameworks such as Next.js or React Router for entire apps, which is why the compiler reaches Vite users through plugin integrations rather than through React core, and why the integration path differs depending on whether you run the plain React plugin or framework mode.
On speed, oxc project lead Boshen’s claim is directional and vendor-run: “It is more than 10 times faster than Babel in our preliminary benchmark,” as quoted in the migration writeup. Preliminary, self-measured, and worth treating as a floor rather than a promise. The field data below is the more useful number.
How do you turn it on?
With @vitejs/plugin-react v6.1.0, the documented switch is one line: pass { compiler: true } to the plugin call in your Vite config, per the writeup. The release labels the support experimental, so pin the plugin version you tested and read its release notes before rolling wide.
The change is subtraction. The Babel path carried a transform stage bolted onto the build to run the compiler; the native path runs inside the official plugin with no Babel stage in between. For a build pipeline, deleting a transform stage is worth more than any option you add.
React Router framework mode
Framework-mode React Router gets the same compiler through a different door. Teams unable to use the Vite React plugin (React Router in framework mode is the writeup’s own example) can use @acusti/vite-plugin-react-compiler, a minimal Vite plugin that React-compiles the codebase regardless of the rest of the build pipeline.
Same underlying Rust compiler, different delivery, because framework mode owns its own Vite pipeline and the plain plugin path does not apply. Note what you are adopting here: the acusti plugin is third-party, and its release cadence is not the official plugin’s. That is a small ongoing dependency decision, not a one-time install.
The plugin’s npm page specifies two constraints worth checking before you adopt. It requires Vite 8 or newer, because its reason for existing is completing the all-native oxc/rolldown pipeline that Vite 8 introduced. And it pins an exact, tested version of oxc-transform-react, whose bindings ride a fast-moving 0.x release train tracking React Compiler main; if you need a different bindings version, you force it through package-manager overrides or resolutions. The page also notes the plugin was extracted from Outlyne’s build tooling, where it runs in production, which makes it the same codebase behind the migration numbers above.
How fast is it really: 17.6× or 2.4×?
The honest number is 2.4×: per the writeup1, the whole build went from 22.1s to 9.3s, while the 17.6× headline (14.3s to 0.81s, single-threaded, across 1,036 files1) measures only the compiler stage.
The arithmetic is unglamorous and worth doing yourself. The compiler was about 65% of that build’s wall time (14.3 of 22.1 seconds1), so even deleting the stage outright caps the whole-build gain near 2.8×. The migration landed at 2.4×, with 0.81s of compiler work remaining. Now run it on your build: if your compiler stage is a tenth of your build, an infinitely fast compiler returns you exactly that tenth and no more. The author flags this directly, cautioning that the overall improvement “won’t be nearly as dramatic” as the compiler-stage figure.
Two speed numbers exist and they are not in conflict, but they are not the same measurement either. Boshen’s “>10×” is oxc’s own preliminary benchmark; the 17.6× is one practitioner’s field result against Babel-compiler v1.0. Both point the same direction. Neither has independent replication as of 2026-09-05, and the 17.6× is the quotable figure while the 2.4× is the one your CI bill actually notices.
What does the Rust compiler compile that Babel v1.0 doesn’t?
Three patterns that the Babel-based compiler v1.0 still skips now compile: conditional logic inside try/catch blocks, reassigning a destructured prop inside a nested closure, and computed object property keys.
The practical meaning of a skip is that the function passes through untouched: no memoization, no error, no marker in the output. A component that throws a catch-path branch into a try block, or that mutates a destructured prop a closure later reads, previously just stayed hand-written. Under the Rust compiler those functions get the same treatment as the rest of the file, per the writeup.
The measured delta in the author’s app is modest and worth internalizing: seven additional functions compiled, five from the try/catch fix and two from computed object keys. Seven functions in a 1,036-file codebase1 is not a coverage leap; it is the difference between “compiles the codebase except the structurally awkward parts” and “compiles the codebase.” How much you gain depends entirely on how much try/catch-heavy and computed-key-heavy code you have. Audit yours; do not extrapolate from seven.
Where does manual useMemo still belong?
Anywhere the compiler declines to run. The boundary moves release to release: conditional logic in try/catch, reassigned destructured props, and computed keys were all skips until this release compiled them. Design around the skip list attached to the version you actually run, not around a list you read once.
This is the part of the decision that does not survive a switch flip. The skip is silent at build time. The component compiles to itself, nothing errors, and absent lint coverage nothing warns.
The workflow that follows is mechanical. Before enabling, inventory the codebase against the skip list for the compiler version you are adopting. For each hit, decide: refactor the pattern away, or leave it and memoize by hand. Keep manual memoization where the pattern is load-bearing rather than incidental; contorting working code to appease a compiler is a bad trade.
The phrase “memoization without useMemo” holds wherever the compiler runs. The exceptions are whatever it still skips, and the only way to know them is to look.
Keep lint and build on one compiler version
Anywhere lint rules and build transforms derive from one fast-moving compiler, pin them together. The skip list changes version to version (this release alone moved three patterns from skipped to supported), which means lint and build can disagree about what compiles if they resolve different versions. Concretely: resolve both versions in CI and fail the build on mismatch. The lockfile gets you most of the way; transitive dependencies get you the rest. This is also the strongest argument for the native path over mixed setups, since a single compiler serving both roles removes the skew class rather than merely warning about it.
What does it cost in CI and toolchain weight?
You take on a Rust toolchain dependency in the pipeline in exchange for a much cheaper compile stage, at a moment when CI minutes are being counted like a budget line.
The cost driver, in the author’s framing, is agent-assisted development: CI usage and GitHub Actions minutes have become “a real cost center.” Agent-driven workflows multiply pipeline invocations, since every attempted fix builds the project, so a 2.4× whole-build cut compounds well beyond developer patience. Treat that framing as one practitioner’s observation rather than an industry measurement; the direction is plausible even where the accounting differs.
The second-order shift is where the actual work goes. Nobody writes memo code in compiled components anymore. The remaining render-performance work is auditing what the compiler declines, keeping the skip list short, and holding lint and build on one version. That is a smaller job than the old one, but it is a job, and it lives in build policy and CI checks rather than in code review.
Who should enable it today, and who should wait?
Enable it today if you run the standard React plugin and can carry an experimental label; wait if your audit finds skip-heavy patterns all over hot paths.
| Setup | Best for | What you gain | Migration effort | Caveats |
|---|---|---|---|---|
@vitejs/plugin-react v6.1.0 with { compiler: true } | Apps on the standard React plugin | Compiler stage 14.3s to 0.81s in the source migration1; three more JS patterns compiled | One config flag in the plugin call | Labeled experimental; evidence from one 1,036-file codebase1 |
@acusti/vite-plugin-react-compiler | React Router framework mode | Same Rust compiler, no Babel stage required | Add one third-party plugin | Third-party plugin on its own release cadence; requires Vite 8 or newer |
| Stay on the Babel plugin chain | Teams requiring only stable paths | Nothing new | None | v1.0 still skips try/catch conditionals, reassigned destructured props, and computed keys |
Three things would move the wait column into the enable column: the experimental label lifting, the skip list shrinking further, and an independent benchmark appearing. None is guaranteed on any timetable, which is the ordinary condition of adopting build tooling in its first year.
Verdict: flip it, then audit what it skips
With @vitejs/plugin-react v6.1.0, pass { compiler: true } and delete the Babel chain; framework-mode React Router codebases get the same result through @acusti/vite-plugin-react-compiler. It is faster, simpler, and compiles strictly more code than the Babel v1.0 path. But treat enabling as the start of a coverage audit, not the end of the work: inventory the functions that still skip, keep manual memoization or refactor there, and pin lint and build to the same compiler version so their output cannot drift.
The strongest limitation, restated because it does not age out during a review cycle: every performance and coverage number in this piece traces to one practitioner’s 1,036-file codebase1 plus oxc’s own preliminary benchmark, the Babel comparison targets compiler v1.0, and the Vite integration is explicitly experimental. If that evidence base does not meet your risk bar, the Babel path still works today. It just does not get the coverage fixes above, which is its own kind of decision.
Recheck next quarter: whether the experimental label lifts, whether the skip list keeps shrinking, and whether anyone independent replicates the speed numbers.
Memoization became build policy the day the option landed in the official plugin. The remaining engineering question is narrower than the old one, and easier to answer: what does the compiler refuse, and did you check?
Frequently Asked Questions
Does the Rust compiler handle logical assignment operators like ??= or &&=?
No, the compiler currently skips components or hooks that use logical assignment operators (??=, &&=, ||=). These patterns remain on the skip list, meaning developers must continue to use manual memoization or refactor the code to avoid these operators if they want automatic optimization.
How does the native Vite plugin path differ from the Babel plugin chain in terms of dependency management?
The native path requires installing oxc-transform-react and removing Babel-related dependencies like @rolldown/plugin-babel or babel-plugin-react-compiler. This simplifies the dependency tree by eliminating the Babel transform stage entirely, whereas the Babel path requires maintaining a separate transform stage and its associated presets.
What is the risk of version skew between Oxlint and the build compiler?
Version skew can cause silent coverage gaps where the linter uses a newer compiler version that supports certain patterns, while the build uses an older version that skips them. This results in components being unoptimized in production without triggering any lint errors, leading to wasted-render regressions that are only discovered later via profiling.
Is the 17.6x speedup figure applicable to the entire build process?
No, the 17.6x speedup applies only to the compiler stage of the build. The overall build time improvement is approximately 2.4x, as the compiler is only one part of the total build process. Teams should expect the whole-build gain to be significantly lower than the compiler-stage figure, depending on the proportion of time spent on other build tasks.
What are the specific JavaScript patterns that the Rust compiler now supports that Babel v1.0 did not?
The Rust compiler now supports conditional logic inside try/catch blocks, reassigning destructured props in nested closures, and computed object property keys. These patterns were previously skipped by the Babel-based compiler v1.0, meaning they required manual memoization or refactoring to ensure proper optimization.