Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions packages/plugin-react/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,25 @@

## Unreleased

### Add experimental native React Compiler support ([#1419](https://github.com/vitejs/vite-plugin-react/pull/1419))

Add experimental native React Compiler support.

You can use it by installing `oxc-transform-react` and enabling it via the `compiler` option:
```sh
npm install -D oxc-transform-react
```
```js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
plugins: [
react({ compiler: true })
]
})
```

## 6.0.5 (2026-07-30)

### Fixed the react compiler preset filter to be linear ([#1353](https://github.com/vitejs/vite-plugin-react/pull/1353))
Expand Down
33 changes: 31 additions & 2 deletions packages/plugin-react/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,42 @@ Under the hood, this simply updates the React Fash Refresh runtime URL from `/@r

## React Compiler

[React Compiler](https://react.dev/learn/react-compiler) support is available via the exported `reactCompilerPreset` helper, which requires [`@rolldown/plugin-babel`](https://npmx.dev/package/@rolldown/plugin-babel) and [`babel-plugin-react-compiler`](https://npmx.dev/package/babel-plugin-react-compiler) and [`@babel/core`](https://npmx.dev/package/@babel/core) as peer dependencies:
### Rust React Compiler

> [!WARNING]
> Native React Compiler support is experimental.

The `compiler` option uses [`oxc-transform-react`](https://npmx.dev/package/oxc-transform-react), a Rust port of [React Compiler](https://react.dev/learn/react-compiler), which must be installed as an optional peer dependency:

```sh
npm install -D oxc-transform-react
```

```js
// vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
plugins: [react({ compiler: true })],
})
```

The `compiler` option also accepts [React Compiler options](https://react.dev/reference/react-compiler/configuration):

```js
react({ compiler: { compilationMode: 'annotation' } })
```

### Babel React Compiler

React Compiler can also be used through Babel with the exported `reactCompilerPreset` helper. This requires [`@rolldown/plugin-babel`](https://npmx.dev/package/@rolldown/plugin-babel), [`babel-plugin-react-compiler`](https://npmx.dev/package/babel-plugin-react-compiler), and [`@babel/core`](https://npmx.dev/package/@babel/core) as peer dependencies:

```sh
npm install -D @rolldown/plugin-babel @babel/core babel-plugin-react-compiler
```

If you are using TypeScript, you will also need to install [`@types/babel__core`](https://npmx.dev/package/@types/babel__core):
If you are using TypeScript with Babel 7, you will also need to install [`@types/babel__core`](https://npmx.dev/package/@types/babel__core):

```sh
npm install -D @types/babel__core
Expand Down
5 changes: 5 additions & 0 deletions packages/plugin-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"@rolldown/plugin-babel": "^0.2.3",
"@vitejs/react-common": "workspace:*",
"babel-plugin-react-compiler": "^1.0.0",
"oxc-transform-react": "^0.145.0",
"react": "^19.2.8",
"react-dom": "^19.2.8",
"rolldown": "^1.2.3",
Expand All @@ -59,6 +60,7 @@
"peerDependencies": {
"@rolldown/plugin-babel": "^0.1.7 || ^0.2.0",
"babel-plugin-react-compiler": "^1.0.0",
"oxc-transform-react": "^0.145.0",
"vite": "^8.0.0"
},
"peerDependenciesMeta": {
Expand All @@ -67,6 +69,9 @@
},
"babel-plugin-react-compiler": {
"optional": true
},
"oxc-transform-react": {
"optional": true
}
},
"engines": {
Expand Down
126 changes: 122 additions & 4 deletions packages/plugin-react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import {
} from '@vitejs/react-common'
import type { Plugin, ServerOptions } from 'vite'
import { reactRefreshWrapperPlugin } from 'vite/internal'
import { reactCompilerPreset } from './reactCompilerPreset'
import type { ReactCompilerOptions } from '#optionalTypes'
import { defaultCodeFilter, reactCompilerPreset } from './reactCompilerPreset'

const _dirname = dirname(fileURLToPath(import.meta.url))
const refreshRuntimePath = join(_dirname, 'refresh-runtime.js')
Expand Down Expand Up @@ -52,6 +53,13 @@ export interface Options {
* reactRefreshHost: 'http://localhost:3000'
*/
reactRefreshHost?: string
/**
* Enable React Compiler with its default options or configure it.
* This requires `oxc-transform-react` to be installed.
* @default false
Comment thread
sapphi-red marked this conversation as resolved.
* @experimental
*/
compiler?: boolean | ReactCompilerOptions
}

const defaultIncludeRE = /\.[tj]sx?$/
Expand Down Expand Up @@ -82,12 +90,13 @@ export default function viteReact(opts: Options = {}): Plugin[] {
name: 'vite:react-babel',
enforce: 'pre',
config(_userConfig, { command }) {
const refresh = command === 'serve' && !opts.compiler
if (opts.jsxRuntime === 'classic') {
return {
oxc: {
jsx: {
runtime: 'classic',
refresh: command === 'serve',
refresh,
},
jsxRefreshInclude: makeIdFiltersToMatchWithQuery(include),
jsxRefreshExclude: makeIdFiltersToMatchWithQuery(exclude),
Expand All @@ -99,7 +108,7 @@ export default function viteReact(opts: Options = {}): Plugin[] {
jsx: {
runtime: 'automatic',
importSource: opts.jsxImportSource,
refresh: command === 'serve',
refresh,
},
jsxRefreshInclude: makeIdFiltersToMatchWithQuery(include),
jsxRefreshExclude: makeIdFiltersToMatchWithQuery(exclude),
Expand Down Expand Up @@ -251,7 +260,7 @@ export default function viteReact(opts: Options = {}): Plugin[] {
},
}

return [
const plugins = [
viteBabel,
viteRefreshWrapper,
viteConfigPost,
Expand All @@ -262,11 +271,120 @@ export default function viteReact(opts: Options = {}): Plugin[] {
isEnabled: () => !skipFastRefresh && !isBundledDev,
}),
]

if (opts.compiler) {
plugins.unshift(
createReactCompilerPlugin(
opts.compiler === true ? {} : opts.compiler,
include,
exclude,
opts,
() => !skipFastRefresh,
),
)
}

return plugins
}

function createReactCompilerPlugin(
options: ReactCompilerOptions,
include: NonNullable<Options['include']>,
exclude: NonNullable<Options['exclude']>,
reactOptions: Pick<Options, 'jsxRuntime' | 'jsxImportSource'>,
isFastRefreshEnabled: () => boolean,
): Plugin {
let sourcemap = true
let jsxDevelopment = false
let compiler: typeof import('oxc-transform-react') | undefined
const runtime =
options.target === '17' || options.target === '18'
? 'react-compiler-runtime'
: 'react/compiler-runtime'

const loadCompiler = async (
onError: (message: string) => never,
): Promise<typeof import('oxc-transform-react')> => {
if (compiler) return compiler

try {
return (compiler = await import('oxc-transform-react'))
} catch (error) {
return onError(
`React Compiler requires the optional \`oxc-transform-react\` package. Install it in your project before enabling \`react({ compiler: true })\`.${
error instanceof Error ? `\n${error.message}` : ''
}`,
)
}
}

return {
name: 'vite:react-compiler',
enforce: 'pre',
async config() {
await loadCompiler((message) => this.error(message))
return {
optimizeDeps: {
include: [runtime],
},
}
},
configResolved(config) {
sourcemap = config.command !== 'build' || !!config.build.sourcemap
jsxDevelopment = !config.isProduction
},
transform: {
filter: {
id: {
include: makeIdFiltersToMatchWithQuery(include),
exclude: makeIdFiltersToMatchWithQuery(exclude),
},
},
async handler(code, id) {
const isClient = this.environment?.config.consumer !== 'server'
const shouldCompile =
isClient &&
(options.compilationMode === 'annotation'
? /['"]use memo['"]/.test(code)
: defaultCodeFilter.test(code))
// The config hook is not called when the plugin is used with Rolldown directly.
const { transform } =
compiler ?? (await loadCompiler((message) => this.error(message)))

const result = await transform(id.split('?')[0]!, code, {
jsx: {
runtime: reactOptions.jsxRuntime,
development: jsxDevelopment,
importSource: reactOptions.jsxImportSource,
refresh: isClient && isFastRefreshEnabled(),
},
reactCompiler: shouldCompile ? options : false,
sourcemap,
})
const diagnostics = result.errors.map(
(error) =>
`${error.message}${error.codeframe ? `\n${error.codeframe}` : ''}`,
)

if (result.fatal) {
this.error(
diagnostics.join('\n\n') || 'React Compiler transform failed.',
)
}
for (const diagnostic of diagnostics) {
this.warn(diagnostic)
}

return { code: result.code, map: result.map }
},
},
}
}

viteReact.preambleCode = preambleCode

export { reactCompilerPreset }
export type { ReactCompilerOptions }

// Compat for require
function viteReactForCjs(this: unknown, options: Options): Plugin[] {
Expand Down
Loading
Loading