Skip to content

feat(react): add native React Compiler support - #1419

Open
Boshen wants to merge 8 commits into
vitejs:mainfrom
Boshen:agent/react-compiler
Open

feat(react): add native React Compiler support#1419
Boshen wants to merge 8 commits into
vitejs:mainfrom
Boshen:agent/react-compiler

Conversation

@Boshen

@Boshen Boshen commented Aug 9, 2026

Copy link
Copy Markdown
Member

Add a compiler option backed by the optional oxc-transform-react package.

When enabled, oxc-transform-react owns the React Compiler, TypeScript/JSX, and Fast Refresh transforms in one pass, while Rolldown React transforms are disabled. React Compiler and Fast Refresh remain client-only; server environments use the same package for TypeScript/JSX.

Keeping React Compiler in a separate package avoids adding framework-specific compiler size and complexity to Rolldown.

@Boshen Boshen changed the title feat(react): add built-in React Compiler support feat(react): add native React Compiler support Aug 10, 2026
@Boshen
Boshen force-pushed the agent/react-compiler branch from 0aaf260 to 89c2017 Compare August 10, 2026 01:57
Comment thread packages/plugin-react/src/index.ts Outdated
Comment thread packages/plugin-react/src/index.ts Outdated
@Boshen
Boshen marked this pull request as ready for review August 10, 2026 08:56

@ArnaudBarre ArnaudBarre left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if we would not have better stability by always using oxc-transform-react. You could then remove react refresh from the builtin oxc-transform, making it a lot more vendor neutral.
That's a trade off, because it would be a bit slower for people not using the compiler, but in my experience the overhead is not visible in real Vite application (and in build mode without compiler, the builtin transform could still be used)

Comment thread packages/plugin-react/src/index.ts Outdated
compiler ?? (await loadCompiler((message) => this.error(message)))

const result = await transform(id.split('?')[0]!, code, {
jsx: 'preserve',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having this plugin doing this transformation with JSX preserve and then the builtin rolldown doing the jsx transformation will make the jsxDev output have wrong line numbers. This breaks various plugin and browser extensions that are using this to jump from the client to the editor in dev mode.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re both comments:

oxc-transform-react is intended to provide or react related transforms.

For @vitejs/plugin-react, I want to keep the blast radius minimal so it only uses the react compiler transform from oxc-transform-react.

Having this plugin doing this transformation with JSX preserve and then the builtin rolldown doing the jsx transformation will make the jsxDev output have wrong line numbers.

Is this a bug that I should fix? Does it happen with babel react compiler as well?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It happens when people configure Babel to only use the react compiler, but it can be fixed by telling Babel to also handle jsx transform, which can't be done with the current setup

"peerDependencies": {
"@rolldown/plugin-babel": "^0.1.7 || ^0.2.0",
"babel-plugin-react-compiler": "^1.0.0",
"oxc-transform-react": "^0.144.0",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whenever, oxc bumps its minor version, we have to release plugin-react to solve the peer dep error / warning. We can live with it for a while but it's not ideal.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whenever, oxc bumps its minor version, we have to release plugin-react to solve the peer dep error / warning.

Can you explain how this happens?

Comment thread packages/plugin-react/README.md Outdated
@Boshen

Boshen commented Aug 17, 2026

Copy link
Copy Markdown
Member Author

Release plan: merge this integration PR, then after today's Oxc release (v0.145.0), do a bump then a formal release.

@Boshen

Boshen commented Aug 17, 2026

Copy link
Copy Markdown
Member Author

@sapphi-red handing over to you and the team.

Comment thread packages/plugin-react/README.md Outdated
Comment on lines +92 to +99
if (opts.compiler) {
return {
oxc: { jsx: 'preserve' },
optimizeDeps: {
rolldownOptions: { transform: { jsx: 'preserve' } },
},
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should not touch the oxc config here

  • If the compiler ran, there is no jsx anymore so it doesn't matter
  • If the compiler didn't run because of the include/exclude, the jsx should still be transformed to not ship it to the browser

I think the same idea also apply for the two others compiler opts.compiler conditions

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My intent is for oxc-transform-react to own React Compiler, TypeScript/JSX, and Fast Refresh in one pass. Vite/Rolldown’s React transforms are disabled to avoid duplicate transforms.

Can you advice on the correct change you are proposing?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the file was already transform by oxc-transform-react then there is no risk of duplicate work for the JSX transform. For fast refresh, it may indeed induce duplicated work.
It's important to not disable JSX transform all together, because there are some users that are using the include/exclude query to opt out of fast refresh transform but still expect jsx transform (when generating PDF from react component in workers for example)

Here is my suggestion that takes into account both things:

    config(_userConfig, { command }) {
      const refresh = command === 'serve' && opts.compiler
      if (opts.jsxRuntime === 'classic') {
        return {
          oxc: {
            jsx: { runtime: 'classic', refresh },
            jsxRefreshInclude: refresh ? makeIdFiltersToMatchWithQuery(include) : undefined,
            jsxRefreshExclude: refresh ? makeIdFiltersToMatchWithQuery(exclude) : undefined,
          },
        }
      } else {
        return {
          oxc: {
            jsx: {
              runtime: 'automatic',
              importSource: opts.jsxImportSource,
              refresh,
            },
            jsxRefreshInclude: refresh ? makeIdFiltersToMatchWithQuery(include) : undefined,
            jsxRefreshExclude: refresh ? makeIdFiltersToMatchWithQuery(exclude) : undefined,
          },
          optimizeDeps: {
            rolldownOptions: { transform: { jsx: { runtime: 'automatic' } } },
          },
        }
      }
    },

Co-authored-by: Arnaud Barré <arnaud.barre72@gmail.com>
userConfig.server?.hmr,
)
if (skipFastRefresh) {
if (skipFastRefresh && !opts.compiler) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If compiler is enabled, this should be false already and I prefer to keep this condition simple instead of optimizing for one config merge in the rare usecase covered by this plugin (hmr disabled by plugin IIRC)

Suggested change
if (skipFastRefresh && !opts.compiler) {
if (skipFastRefresh) {

Comment on lines +150 to +152
options.transform.jsx = opts.compiler
? 'preserve'
: {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
options.transform.jsx = opts.compiler
? 'preserve'
: {
options.transform.jsx = {

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants