Skip to content

Instantly share code, notes, and snippets.

@sikanhe
Last active January 6, 2026 12:38
Show Gist options
  • Select an option

  • Save sikanhe/f9ac68dd4c78c914c29cc98e7b875466 to your computer and use it in GitHub Desktop.

Select an option

Save sikanhe/f9ac68dd4c78c914c29cc98e7b875466 to your computer and use it in GitHub Desktop.
React Compiler plugin for ESBuild
import { readFileSync } from "node:fs"
import * as babel from "@babel/core"
import BabelPluginReactCompiler from "babel-plugin-react-compiler"
import type { Plugin } from "esbuild"
import QuickLRU from "quick-lru"
export function ReactCompilerEsbuildPlugin({
filter,
sourceMaps,
runtimeModulePath,
}: { filter: RegExp; sourceMaps: boolean; runtimeModulePath: string }): Plugin {
return {
name: "esbuild-react-compiler-plugin",
setup(build) {
// Cache previous outputs for incremental rebuilds
const buildCache = new QuickLRU<string, string>({ maxSize: 1000 })
let timings: number[] = []
build.onEnd(() => {
if (timings.length < 1) return
const totalTime = timings.reduce((sum, x) => sum + x, 0).toFixed(0)
console.log(`[⚛️ React Compiler] ${timings.length} files changed`)
console.log(`[⚛️ React Compiler] Used ${totalTime} ms`)
timings = []
})
build.onLoad({ filter, namespace: "" }, (args) => {
const contents = readFileSync(args.path, "utf8")
const t0 = performance.now()
if (buildCache.has(contents)) {
return {
contents: buildCache.get(contents),
loader: "js",
}
}
const output = build.esbuild.transformSync(contents, {
loader: "tsx",
jsx: "automatic",
define: build.initialOptions.define,
target: build.initialOptions.target,
})
const transformResult = babel.transformSync(output.code, {
plugins: [
// Warning: using string config here (ie 'babel-plugin-react-compiler') instead of the directly
// imported object is much slower than directly passing the plugin object because
// Babel has to resolve the plugin file from node_modules
[
BabelPluginReactCompiler,
{
runtimeModule: runtimeModulePath,
},
],
],
filename: args.path,
caller: {
name: "esbuild-react-compiler-plugin",
supportsStaticESM: true,
},
// TODO: figure out sourcemap setting and chaining
sourceMaps,
})
timings.push(performance.now() - t0)
if (transformResult?.code) {
buildCache.set(contents, transformResult?.code)
}
return {
contents: transformResult?.code ?? undefined,
loader: "js",
}
})
},
}
}
@mbrevda
Copy link

mbrevda commented Jan 6, 2026

What is the performance penalty of using this?

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