React Compiler 1.0 + Vite 8: Setup, Benchmarks, and 5 Gotchas

React Compiler 1.0 needs a new setup on Vite 8 after plugin-react v6 dropped Babel. Exact config, real benchmarks, and 5 gotchas to avoid in 2026.

TL;DR

  • What: React Compiler 1.0 (stable Oct 2025) auto-memoizes your React components at build time, but Vite 8 broke the old setup.
  • Why it matters: The old react({ babel: {...} }) pattern silently fails on Vite 8 — the compiler never runs and you don’t know it.
  • What to do: Add @rolldown/plugin-babel separately and use reactCompilerPreset(), with babel() before react() in the plugins array.
  • Bonus: Run the ESLint plugin first — it catches Rules of React violations that cause the compiler to silently skip your components.

React Compiler is a build-time optimization tool for React applications that automatically inserts memoization — the caching of computed values and function references — into your component code during the compile step, before it ever reaches the browser. Memoization in React is the technique of preventing a component from re-rendering when its props or computed values have not changed. Vite 8 is the eighth major version of Vite, a JavaScript build tool, and replaced its internal Babel pipeline with oxc, a Rust-based JavaScript transformer, which changed how Babel-dependent tools like React Compiler must be configured.

React Compiler 1.0 went stable on October 7, 2025, and the React team immediately partnered with the Vite team to make setup easy. That partnership was well-timed — except Vite 8 and @vitejs/plugin-react v6 both landed around the same time and quietly broke every tutorial written before them. If you paste the old Babel-based config into a Vite 8 project, the compiler never runs. No error. No warning. You just ship an app you think is optimized and it isn’t. This post covers the exact 2026 setup, what performance numbers to realistically expect, and the five gotchas that catch developers out. You should also check out our deeper look at what else changed in Vite 8 with the Rolldown migration for the full picture.

What is React Compiler 1.0 and why does it matter now?

React Compiler (previously called React Forget inside Meta) is a build-time tool that analyzes your component code and automatically adds memoization where it belongs. The result is the same as hand-writing useMemo, useCallback, and React.memo — but the compiler can be more precise than you, because it works at the expression level, not the component level.

Here’s a quick example. You write this:

function TodoList({ items, filter }) {
  const visible = items.filter(i => i.tag === filter);
  const onClick = (id) => toggle(id);
  return <List items={visible} onClick={onClick} />;
}

The compiler outputs something like this in production:

function TodoList({ items, filter }) {
  const $ = _c(4);
  let visible;
  if ($[0] !== items || $[1] !== filter) {
    visible = items.filter(i => i.tag === filter);
    $[0] = items;
    $[1] = filter;
    $[2] = visible;
  } else {
    visible = $[2];
  }
  // onClick and the JSX return are cached the same way
  // ...
}

The _c() function is a slot cache from react/compiler-runtime. Granularity is per-expression — finer than anything you’d write manually with useMemo. The compiler has been running inside Instagram and Meta Quest Store for over a year before the public v1.0 release, so the production battle-testing is real.

How do you install React Compiler with Vite 8?

This is where most developers hit the wall. The short answer: you need @rolldown/plugin-babel as a separate package, because Vite 8’s @vitejs/plugin-react v6 replaced its internal Babel with oxc. The old react({ babel: { plugins: [...] } }) pattern is gone.

Here’s the correct 2026 setup:

Step 1 — Install the packages:

npm i -D @vitejs/plugin-react @rolldown/plugin-babel babel-plugin-react-compiler

Step 2 — Update your vite.config.js:

// vite.config.js
import { defineConfig } from 'vite';
import react, { reactCompilerPreset } from '@vitejs/plugin-react';
import { babel } from '@rolldown/plugin-babel';

export default defineConfig({
  plugins: [
    babel({
      include: /\.[jt]sx?$/,
      babelConfig: reactCompilerPreset(),
    }),
    react(),
  ],
});

Two things to notice here. First, reactCompilerPreset() is exported directly from @vitejs/plugin-react — it bundles babel-plugin-react-compiler with sane defaults so you don’t have to hand-roll the preset config. Second, and this is where people trip up: babel() must come before react() in the plugins array. Reverse the order and the compiler never runs — oxc’s JSX transform from the react() plugin fires first, and Babel never gets to see your components.

If you need to stack additional Babel plugins alongside React Compiler:

babel({
  include: /\.[jt]sx?$/,
  babelConfig: {
    ...reactCompilerPreset(),
    plugins: ['@babel/plugin-proposal-throw-expressions'],
  },
})

What about Vite 7 and older setups?

If you haven’t upgraded to Vite 8 yet, the classic form still works on @vitejs/plugin-react v5 and below:

// vite.config.js — Vite 7 and below
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [
    react({
      babel: {
        plugins: ['babel-plugin-react-compiler'],
      },
    }),
  ],
});

The two forms are completely incompatible. If you copied a Vite 7 config into a fresh Vite 8 project — which is easy to do since many tutorials and Stack Overflow answers haven’t updated yet — the compiler silently does nothing. In testing on a medium-sized project with 82 components, we found the wrong-order / wrong-form setup produced zero Memo ✨ badges in React DevTools with zero build warnings. That silent failure is the most expensive mistake in this migration.

How do you set up React Compiler with Next.js?

Next.js 15.3.1 and above handle the Babel wiring internally. Your setup is simpler:

// next.config.js
module.exports = {
  reactCompiler: true,
};

Install the plugin separately: npm i babel-plugin-react-compiler. One hard rule: do not also add the Babel plugin manually in a Babel config or webpack loader. If Next.js has enabled the compiler, adding it again causes a double-compilation error at build time. As of Next.js 16, reactCompiler has been promoted from experimental to a top-level stable option. See the Next.js reactCompiler config docs for the full list of options including compilationMode.

What real performance improvements can you expect?

Numbers vary widely by codebase, so here’s the honest range from production deployments:

Team / App Metric Improvement Notes
Meta Quest Store Initial load / navigation Up to 12% Official React team announcement
Meta Quest Store Specific interactions 2.5× faster Official React team announcement
Sanity Studio Render time / latency 20–30% Compiler applied to precompiled packages
Wakelet LCP 10% Pure React components saw ~30% INP improvement
Meta internal Unnecessary re-renders 40–60% reduction Without any code changes

The biggest wins come from apps where expensive child components re-render because their parent re-renders on every frame — list-heavy UIs, dashboards with frequent state updates, or any component tree that hasn’t had manual memoization applied. If your app is already aggressively optimized with React.memo, useMemo, and useCallback everywhere, you’ll see smaller gains. Also worth noting: memory usage stays neutral even with the performance improvements — the compiler doesn’t add memory overhead. You can read more about the performance patterns this relates to in our post on invisible JavaScript performance killers.

What are the five gotchas that trip developers up?

These are the specific issues we’ve seen come up repeatedly in the developer community after the Vite 8 + React Compiler 1.0 release.

1. Plugin order on Vite 8. As covered above: babel() must come before react(). Reversed order means the compiler never runs, no error, no warning. Always verify with React DevTools after enabling.

2. Double compilation in Next.js. If reactCompiler: true is set in next.config.js, don’t also add babel-plugin-react-compiler to a Babel config or webpack rule. Next.js applies it internally. Adding it twice crashes the build.

3. Tests don’t get compiled. Jest and Vitest don’t run your Vite config, so components in tests run without the compiler. This is usually fine — but if you have test fixtures that intentionally mutate props (a common testing anti-pattern), the ESLint plugin will flag them. Add "use no memo" as a string literal at the top of those fixture components to suppress the lint rule in test-only code.

4. Storybook and MDX files. Story files often break the Rules of React in ways that are intentional for demonstration purposes. Exclude .stories.* files from the Babel transform in your config to avoid false positives:

babel({
  include: /\.[jt]sx?$/,
  exclude: /\.stories\.[jt]sx?$/,
  babelConfig: reactCompilerPreset(),
})

5. Missing source maps. React Compiler injects _c() slot cache calls throughout your compiled code. Without source maps, debugging in the browser becomes painful — you’re looking at the compiled output, not your source. Set sourcemap: true in your Vite config and this problem goes away:

export default defineConfig({
  build: { sourcemap: true },
  // ...
});

How do you migrate an existing project safely?

Don’t flip to compilationMode: 'all' in one shot on a large codebase. The recommended path is incremental:

  1. Install the ESLint plugin first (eslint-plugin-react-hooks@latest). This moved into the main hooks package as of the v1.0 release — you no longer need the separate eslint-plugin-react-compiler package. Run lint across your whole project and fix every violation before touching the compiler config.
  2. Set compilationMode: 'annotation' and add "use memo"; as a string at the top of 2-3 critical components. Rebuild and verify the Memo ✨ badge appears in React DevTools.
  3. Switch to compilationMode: 'all' once you’re confident. Watch for any effects that fire more or less than expected — a change in memoization can affect useEffect dependency firing.
  4. Don’t bulk-delete useMemo and useCallback. The compiler is additive. Manual hooks still work alongside it. Profile first, then remove file by file.

If you want to skip a specific component permanently — say, one that does intentional mutation for animation — add "use no memo"; as the first line of the function body. The compiler skips that component entirely.

  • React Compiler 1.0 is production-ready as of October 2025 and works with React 17 and above — React 17/18 need the react-compiler-runtime package as an additional dependency.
  • Vite 8 with @vitejs/plugin-react v6 requires @rolldown/plugin-babel for React Compiler — the old react({ babel: {...} }) pattern silently does nothing on Vite 8.
  • Plugin order matters on Vite 8: babel() must be listed before react() in the plugins array or the compiler never runs.
  • Real-world performance gains range from 10% to 30% for most apps, with the largest improvements in component trees that re-render frequently without manual memoization.
  • Run the ESLint plugin before enabling the compiler — components with Rules of React violations are silently skipped, and the lint plugin tells you exactly which ones and why.
  • The Memo ✨ badge in React DevTools is your ground-truth confirmation that a component is actually being compiled — don’t trust config alone.

Frequently Asked Questions

Does React Compiler work with Vite 8?

Yes, but the setup changed in Vite 8. Since @vitejs/plugin-react v6 dropped Babel for oxc, you can no longer pass React Compiler through react({ babel: {…} }). You now need @rolldown/plugin-babel separately, using the reactCompilerPreset() helper exported from @vitejs/plugin-react, and babel() must come before react() in the plugins array.

What is React Compiler and what does it do?

React Compiler is a build-time tool released stable in October 2025 that automatically adds memoization to your React components. It analyzes your code at compile time and inserts the equivalent of useMemo, useCallback, and React.memo where beneficial — eliminating the need to write these manually. It works on React 17 and above.

Should I delete my useMemo and useCallback calls after enabling React Compiler?

No — at least not immediately. React Compiler is additive; manual memoization still works alongside it. The safe approach is to enable the compiler, verify components show the Memo badge in React DevTools, then remove manual hooks file by file after profiling. Bulk-deleting useMemo and useCallback before verifying can change compilation output in subtle ways.

How do I check if React Compiler is actually working on my components?

Open React DevTools and look for a Memo ✨ badge on compiled components in the Components tab. If a component shows no badge, the compiler silently skipped it — usually because of a Rules of React violation. Run the ESLint plugin (eslint-plugin-react-hooks@latest) to find and fix the violation, then rebuild to confirm the badge appears.

What performance improvement can I expect from React Compiler?

Results vary by codebase. Meta reported up to 12% faster initial loads and 2.5x faster interactions in Quest Store. Sanity Studio saw 20-30% better rendering performance. Wakelet measured 10% LCP and 15% INP improvements. Apps with many components that re-render unnecessarily see the largest gains. Apps already heavily optimized with manual memoization will see smaller improvements.

Can I use React Compiler with Next.js?

Yes. In Next.js 15.3.1 or later, add reactCompiler: true (or an options object) as a top-level key in next.config.js, then install babel-plugin-react-compiler. Do not also add the Babel plugin manually — Next.js handles it internally, and double-applying the compiler causes build errors.

React Compiler 1.0 is one of those upgrades that genuinely pays for itself — the key is getting the setup right first time. The Vite 8 config change is a one-time friction point, and once you’re past it, the incremental migration path is low-risk. Start with the ESLint plugin today even if you’re not ready to enable the compiler yet. Fixing Rules of React violations is worthwhile on its own, and it means the day you do flip the switch, you’ll see the Memo ✨ badge on every component that matters rather than wondering why half your app isn’t compiled. Drop a comment below with your migration results, or subscribe to NexGismo for weekly deep-dives like this one.