Skip to content

React Compiler oxc plugin #10048

Closed

Description

@poteto

I wanted to open an issue capturing some of our past discussion on adding support for React Compiler as an oxc plugin.

For context, the React team reached out to both the swc and oxc projects a few months ago to collaborate on an official plugin for both projects to run React Compiler, allowing React apps to eliminate their Babel pipelines if it only existed to run the compiler.

There has been some progress on the swc side which has allowed us to start experimenting with running the compiler in swc in the compiler playground: react/react#32428. More testing is still required, but we've been excited to make progress on this as one of the most requested features for React Compiler is swc and oxc support.

We would love to collaborate on an official plugin for oxc. I understand oxc is not yet the default for Vite, but it would be incredible if we can get React Compiler support in oxc before it does. Please reach out via DM (X, Bluesky, etc) if you're interested in working together on this!

Activity

  1. added theissue type on Mar 25, 2025
  2. yyx990803 commented on Apr 29, 2025

    @yyx990803

    Thanks for reaching out!

    We definitely want to make sure React Compiler can work efficiently with OXC, but I want to clarify a few constraints that are present:

    • The React Compiler logic is currently written as Babel plugins and the primary logic is in JS.
    • Both SWC and OXC are Rust toolchains. Supporting a Babel-compatible JS Plugin API is not very practical.
      • It will likely involve passing entire ASTs back and forth between Rust and JS
      • The core logic will still happen in JS, likely with limited parallelism, depending on how the logic is implemented
      • There will likely be limited, if not negative performance gains compared to an out-of-band approach (i.e. running OXC transforms and plain Babel transforms in separate passes)

    As far as I can tell, SWC's current support for React Compiler involves running Babel via an embedded QuickJS instance inside SWC. I haven't benchmarked this myself but I assume it's not going to be very performant. The main benefit is so that on the surface React Compiler can be used in an SWC-only toolchain without involving Babel, but fundamentally it's far from ideal.

    For OXC, we do not think doing something like this is worth it. In the short term, we recommend end users to use React Compiler directly via a separate Babel pass (e.g. Vite and Vite-based-framework users can configure it in @vitejs/plugin-react's babel option).

    For the long run, we hope we can figure out a way to make React Compiler performant for all users. Our observation is the performance wins with Rust-based toolchains heavily depends on minimizing the amount of compilation work done in JS (which is slower and harder to parallelize). Vanilla React apps with Rolldown/OXC-powered-Vite can be up to 16x faster than Rollup-powered-Vite. The gain will likely be much smaller when React Compiler is involved. If the React team intends for React Compiler to become a default recommendation for all React users, I strongly suggest re-evaluating the potential performance tax it would incur on the React ecosystem.

    I don't think there is a way to make the current React Compiler magically fast from either SWC or OXC side - that change needs to come from React Compiler itself. Some thoughts on this:

    • Porting it to Rust will be very challenging - porting it to native OXC transforms will obviously yield the best performance, but the port will not be straightforward due to how differently Rust and JS works (and how OXC places some constraints on memory management for performance). That said, VoidZero would be open to exploring commercial arrangements to help with this if Meta is interested in having the best possible performance for React Compiler in the long run (likely 20~50x faster than Babel-based implementation based on benchmarks).

    • Rewrite the transforms in Go (much easier for a straight port), and expose ways for Rust toolchains to call it via C FFI. TypeScript team has shown success in porting JS/TS code to Go with decent velocity and behavior alignment.

    • Leaving it in JS: we do have plans to offer an API for "simple transforms in JS" - Rust sends AST directly to JS, where the JS-side uses a magic-string style API to do manipulations. Calls are buffered and sent back to Rust, and the final heavy lifting of string manipulation + source map generation happens all on the Rust side. There are many Vite ecosystem transforms using this approach currently and can be migrated to this approach with massive perf gains. The primary concern is that I am not sure this approach would suffice for React Compiler due to its complexity - but if the complexity is mainly in analysis rather than code generation, then I think this is still something worth exploring.

  3. josephsavona commented on May 2, 2025

    @josephsavona

    Hey @yyx990803, thanks for getting back to us! We appreciate your thoughts on how you'd approach an integration like this from the oxc side.

    I don't think there is a way to make the current React Compiler magically fast from either SWC or OXC side - that change needs to come from React Compiler itself. Some thoughts on this

    Yes, we've previously ported Relay Compiler from JS to Rust so we're familiar with the performance challenges of using JS for tooling and the tradeoffs of the languages involved. I'm personally a huge fan of Rust and previously experimented with a partial port of React Compiler to Rust to explore a more native integration with SWC and OXC. Porting to Rust is more straightforward than you may suspect, but it definitely would take a bunch of time to convert that would take time away from work on features and correctness. We hope to be able to prioritize build performance after the compiler is stable.

    One discovery from our Rust exploration was that unlike JS with ESTree as the de facto standard representation, every tool (Hermes, SWC, OXC, ESBuild, Rome before its fall) has its own internal CST/AST-like representation (and sometimes different string representations!). Also note that ASTs aren't a good fit for React Compiler's low-level transformations, so the compiler internally converts from an AST into our own SSA-based intermediate representation, then compiles back to an AST at the end. We ended up defining our own ESTree-like Rust representation to use in the public API (our AST in, our AST out with the SSA form being purely internal), with the idea that we could have a converter for each integration (SWC <> our format, OXC <> our format, etc).

    Long story short: if we did go forward with a native compiler, we'd likely need help implementing the conversion between the OXC AST and our AST.

    I wonder if we can consider the collaboration and integration on two time scales:

    • The initial stable version of React Compiler will remain JS-based. If OXC becomes the default on a shorter time frame, we'd love to make sure that there's at least a reasonable (if not perfectly optimal) way to use React Compiler in Vite-with-OXC. Our main priority is that it be easy to configure and not impose unreasonable overhead (ie if embedded QuickJS is faster than spinning up a Babel pre-pass, it would be nice to do the faster thing).
    • Long-term, build performance is one of several priorities for us after the stable release. Given that we're continuing to expand the feature set of React Compiler, though, our preference is to achieve native-level performance with our existing JS code (the majority of the code is fully decoupled from Babel). We're partnering closely with the Hermes and Flow teams at Meta to explore using Static Hermes for React Compiler. It's very early, but we're likely to pursue this direction rather than a port to a different language. Regardless of whether we stay in JS or port to Rust, though, we'd likely need to explore the AST conversion step noted above.
  4. yyx990803 commented on Jun 3, 2025

    @yyx990803

    Very nice to hear about the feasibility for a Rust port.

    The initial stable version of React Compiler will remain JS-based. If OXC becomes the default on a shorter time frame, we'd love to make sure that there's at least a reasonable (if not perfectly optimal) way to use React Compiler in Vite-with-OXC. Our main priority is that it be easy to configure and not impose unreasonable overhead (ie if embedded QuickJS is faster than spinning up a Babel pre-pass, it would be nice to do the faster thing).

    I think for the short term, having a separate Babel pass is straightforward enough for Vite users. I haven't really compared the embedded QuickJS perf vs. separate Babel pass, and would love to hear how it's doing for SWC.

    We are definitely be open to collaborate on the AST conversion. Assuming you end up going with Static Hermes, and I am understanding it correctly, the eventual pipeline for React Compiler usage in OXC / Rolldown would be:

    src --(oxc parser)--> oxc AST
      --(oxc-to-rc-ir conversion layer)--> React Compiler IR
        --(React Compiler Static Hermes build)--(rc-ir-to-oxc conversion layer)--> oxc AST
          --(other oxc transforms)--(oxc codegen)--> final code
    

    Is this what you have in mind?

  5. josephsavona commented on Jun 4, 2025

    @josephsavona

    Thanks for the feedback! Re the pipeline — yeah that's the idea, except that the React Compiler public format would be an AST. The IR is purely internal, the conversion from AST to IR and back is complex enough that we want to write it just once.

    I haven't really compared the embedded QuickJS perf vs. separate Babel pass, and would love to hear how it's doing for SWC.

    I don't know if we have perf numbers comparing this in SWC but I'll ask around!

  6. added a commit that references this issue on Oct 23, 2025
  7. blixt commented on Nov 29, 2025

    @blixt

    The React Compiler 1.0 has been out for a while now and since switching away from oxc to be able to use the compiler we've seen real improvements for our users. However, our build times have gone up significantly.

    I think many companies will make the same tradeoff as us because users come first, so just wondering if there's a way we can track the progress on oxc/Rolldown supporting this?

  8. craigmiller160 commented on Dec 21, 2025

    @craigmiller160

    Just adding another vote for this. Adopting the React Compiler is something I would love to do in the near future, however the lack of native tooling support is deeply disappointing. I completely understand there is a massive LOE involved in such an endeavor, but what would be required simply to view progress and activity on this front?

  9. skovhus commented on Dec 22, 2025

    @skovhus
    Contributor

    @yyx990803 any advice on how to set this up?

    I think for the short term, having a separate Babel pass is straightforward enough for Vite users.

  10. acusti commented on Dec 22, 2025

    @acusti

    @skovhus here‘s my setup:

    the relevant deps from package.json

        "devDependencies": {
            "babel-plugin-react-compiler": "^1",
            "@babel/preset-typescript": "^7.27.1",
            "vite": "^7.1.10",
            "vite-plugin-babel": "^1.3.1",
            // ...
        },

    the relevant parts of vite.config.ts

    import { defineConfig } from 'vite';
    import babel from 'vite-plugin-babel';
    
    export default defineConfig({
        plugins: [
            babel({
                babelConfig: {
                    plugins: [
                        [
                            'babel-plugin-react-compiler',
                            { environment: { enableTreatRefLikeIdentifiersAsRefs: true } },
                        ],
                    ],
                    presets: ['@babel/preset-typescript'],
                },
                filter: /\.[jt]sx?$/,
            })
        ],
    });

    adding react compiler that way made my build take around 3× longer, but it’s been worth it for me.

  11. silvenon commented on Dec 24, 2025

    @silvenon

    Actually unless you're doing something special you shouldn't need vite-plugin-babel (or @babel/preset-typescript), it's enough to pass babel-plugin-react-compiler directly to @vitejs/plugin-react through its babel options:

    // vite.config.ts
    import { defineConfig } from 'vite';
    import react from '@vitejs/plugin-react';
    
    export default defineConfig({
      plugins: [
        react({
          babel: {
            plugins: ['babel-plugin-react-compiler'],
          },
        }),
      ],
    });

    The official React Compiler docs have instructions for Vite: https://react.dev/learn/react-compiler/installation#vite

  12. narukeu commented on Jan 22, 2026

    @narukeu

    As far as I can tell, SWC's current support for React Compiler involves running Babel via an embedded QuickJS instance inside SWC. I haven't benchmarked this myself but I assume it's not going to be very performant. The main benefit is so that on the surface React Compiler can be used in an SWC-only toolchain without involving Babel, but fundamentally it's far from ideal.

    @yyx990803 Hi, I recently read your comment regarding SWC supporting the React Compiler, particularly the part about it running Babel by embedding QuickJS. I went to take a look at SWC's codebase to learn how this was implemented, but I couldn't seem to find any place, direct or indirect, where QuickJS is used. Did I look in the wrong place, or is this implementation located within a specific plugin or experimental branch? I'd love to learn more about this architecture if you have any insights. Thanks for your help!

  13. lorand-horvath commented on Jan 28, 2026

    @lorand-horvath

    it's enough to pass babel-plugin-react-compiler directly to @vitejs/plugin-react through its babel options

    @silvenon Have you tried this (adding the babel-plugin-react-compiler) in a vite 8 beta project that has rolldown specifically enabled (so it implicitly uses oxc instead of babel)?
    If I understand correctly, you would need to manually add the separate Babel pass that @yyx990803 mentioned above for the babel-plugin-react-compiler to work correctly. Or is that pass happening automatically?

    Edit: Yeah, it seems to be automatic. So React Compiler is applied via an initial Babel pass (slow JS), then the rolldown/oxc bundling (fast Rust) takes over.

  14. silvenon commented on Jan 28, 2026

    @silvenon

    I was talking only about Rollup Vite, I didn't know that Rolldown Vite worked the same way, it's a good thing you questioned it and tried it out! 💯

    I'm using Vite v8, but I'm very behind on adopting React Compiler because I couldn't lint it, I wasn't aware of any Rust-based linter that contain React Compiler rules, but I found out that oxlint can do this via its JS plugin support.

  15. 2 remaining items

  16. lorand-horvath commented on Jan 28, 2026

    @lorand-horvath

    I'm, in fact, using https://github.com/nabla/vite-plugin-eslint - sorry for the confusion. It all makes sense now.

  17. camc314 commented on Jan 28, 2026

    @camc314
    Contributor

    Maybe I don't get it, but does that mean that React Compiler rules don't integrate properly with eslint when used in Rolldown Vite? I thought it would work out-of-the-box. It should work, linting in JS (not Rust), obviously.

    You should consider the linting and bundling as two separate things (it sounds like here the two are getting muddled up.

    For linting, you can run the react compiler rules using oxlint's custom plugins feature - this will run the react eslint plugin (in javascript), and should produce exactly the same errors/diagnostics as running this in eslint

    For bundling: you can use the babel plugin alongside vite 7 (rollup/esbuild) and vite 8 (rolldown), regardless of which version you use, it will run the JS version of the plugin to transform your react code.

    My point is that you can't make the entire build process work in Rust, if you need to use the React Compiler, which is (for now) written in JS-only and requires Babel to integrate into existing projects.

    This is correct; at present, there's no way to either a; run the react compiler (bundler) without JS or b; run the react compiler (lint rules) without JS. The react compiler is still in active development, which means conforming to it would be a constatntly changing process.

  18. TheAlexLichter commented on Feb 5, 2026

    @TheAlexLichter
    Member

    ICYMI: You can use the React Compiler lint rules (demo repo) via Oxlint's JS Plugin integration.
    This does not make the compiler magically faster but allows you to use the other fast linting rules in a single tool!

  19. AndreiTS commented on Feb 5, 2026

    @AndreiTS

    @TheAlexLichter JS plugins doesn't work with the oxc vscode extension?

  20. camc314 commented on Feb 5, 2026

    @camc314
    Contributor

    @TheAlexLichter JS plugins doesn't work with the oxc vscode extension?

    support for this is coming next week.

  21. KirillTregubov commented on Feb 5, 2026

    @KirillTregubov

    ICYMI: You can use the React Compiler lint rules (demo repo) via Oxlint's JS Plugin integration. This does not make the compiler magically faster but allows you to use the other fast linting rules in a single tool!

    Are there any plans to make configuration easier in the future? currently we need to manually specify all the rules, which is brittle to renaming/additions/deletions.

    Thanks to everyone for the awesome work!

  22. camc314 commented on Feb 5, 2026

    @camc314
    Contributor

    Are there any plans to make configuration easier in the future? currently we need to manually specify all the rules, which is brittle to renaming/additions/deletions.

    We (soft) launched dynamic configs last week. this means you can write code such as

    import someExtendsConfig from 'my-liblrary'
    
    export default defineConfig({
        extends: [someExtendsConfig]
    })
    

    It's up to authors what exists inside someExtendsConfig- but it's javascritpt! so it is very flexible as to what can be achieved. I'll be improving the docs around this, as well as posting about this on X in the coming weeks.

  23. KirillTregubov commented on Feb 5, 2026

    @KirillTregubov

    In the meantime for anyone reading, I compiled a comprehensive list for the React Compiler plugin: TheAlexLichter/oxlint-react-compiler-rules#1

  24. tychota commented on Mar 11, 2026

    @tychota

    ICYMI: You can use the React Compiler lint rules (demo repo) via Oxlint's JS Plugin integration. This does not make the compiler magically faster but allows you to use the other fast linting rules in a single tool!

    The use of the js plugin also makes your lint slower.
    From 0.8s to 4.5s in our case.

    A good path forward IMHO would be to feature gap :

    Good article to read.
    https://react.dev/blog/2025/10/07/react-compiler-1#migrating-from-eslint-plugin-react-compiler-to-eslint-plugin-react-hooks

    For what it is worth, claude based gap analysis : https://gist.github.com/tychota/3e0048c76ab8861c26753fb7a05d0d80

  25. orta commented on Mar 11, 2026

    @orta

    As you pinged this thread, I will add that a rust port has made progress from the React team: https://bsky.app/profile/en-js.bsky.social/post/3mgr6wjfkrs2q

  26. Brooooooklyn commented on Mar 12, 2026

    @Brooooooklyn
    SponsorMember

    I'm also working on an Oxc based React compiler POC, it's almost done. If I hadn't known that @josephsavona was doing the same thing, I would have announced it first lol.
    Since the React team has already started this work, I feel it's more reliable to use/integrate the official implementation.

    My attempt is here: https://github.com/oxc-project/oxc/tree/cursor/react-compiler-rust-port-7c74 I won't be releasing pre-build packages. If you want to try it, you can build it locally yourself.

    Build step:

    • pnpm install
    • pnpm --filter oxlint-app build
    • node ./apps/oxlint/dist/cli.js -c oxlint-react-compiler.json

    Example oxlint-react-compiler.json

    {
      "plugins": ["react-compiler"],
      "rules": {
        "react-compiler/hooks": "error",
        "react-compiler/capitalized-calls": "error",
        "react-compiler/static-components": "error",
        "react-compiler/use-memo": "error",
        "react-compiler/void-use-memo": "error",
        "react-compiler/preserve-manual-memoization": "error",
        "react-compiler/memo-dependencies": "error",
        "react-compiler/incompatible-library": "error",
        "react-compiler/immutability": "error",
        "react-compiler/globals": "error",
        "react-compiler/refs": "error",
        "react-compiler/memoized-effect-dependencies": "error",
        "react-compiler/exhaustive-effect-dependencies": "error",
        "react-compiler/set-state-in-effect": "error",
        "react-compiler/no-deriving-state-in-effects": "error",
        "react-compiler/error-boundaries": "error",
        "react-compiler/purity": "error",
        "react-compiler/set-state-in-render": "error",
        "react-compiler/invariant": "error",
        "react-compiler/todo-rule": "error",
        "react-compiler/syntax": "error",
        "react-compiler/unsupported-syntax": "error",
        "react-compiler/compiler-config": "error",
        "react-compiler/gating": "error",
        "react-compiler/rule-suppression": "error",
        "react-compiler/fbt": "error"
      }
    }

    Performance:

    Found 40 warnings and 24 errors.
    Finished in 508ms on 844 files with 82 rules using 16 threads.
    
  27. ArnaudBarre commented on Jun 3, 2026

    @ArnaudBarre
    Contributor

    There is an alpha version to try out: react/react#36173 (comment)
    maybe good to check how react_compiler_oxc::transform() will impact the binary size of oxc-transform if included by default (like react refresh)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-transformerArea - Transformer / Transpiler

    Fields

    Priority

    None yet

    Start date

    None yet

    Target date

    None yet

    Effort

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions