This New React Feature Will Make Your App 20% Faster With Zero Code Changes.
React is evolving again. This time, it’s not just another hook or context API tweak: React compiler

What Is the React Compiler?
The React Compiler (formerly React Forget) is an optimizing compiler that analyzes your React components and automatically memoizes computations, props, and state transitions, the same things you’ve been manually optimizing with useMemo, useCallback, and React.memo.
But here’s the kicker: you don’t have to write any of that boilerplate anymore. The compiler does it for you at build time.
The compiler uses static analysis to understand your component’s data flow, then rewrites your code to eliminate unnecessary re-renders and redundant calculations. It’s like having a senior React performance engineer silently refactoring your entire codebase while you sleep.
Why This Matters
Let’s be honest: React performance optimization has always been a manual, error-prone chore.
You’ve probably written code like this countless times:
const expensiveValue = useMemo(() => computeExpensiveThing(data), [data]);
const handleClick = useCallback(() => doSomething(id), [id]);
Yet inevitably, dependency arrays get missed. Or unnecessary ones get added. Sometimes you wrap components in React.memo when they don’t need it, or fail to wrap the ones that genuinely would benefit. These mistakes are practically unavoidable. This mental overhead is significant and decreases your productivity.
How It Works (Simplified)
The compiler doesn’t use magic. It uses math.
It builds a control-flow graph of your component, tracks how values derive from props and state, and identifies which values remain “stable” across renders. If a value hasn’t changed, the compiler ensures it’s reused, no re-computation, no re-render.
It even handles tricky cases:
Functions passed as props? Automatically stabilized.
Objects and arrays? Memoized if their contents haven’t changed.
Complex hooks and context? Tracked and optimized.
And if your code is too dynamic for static analysis? The compiler gracefully falls back. Your app still works, just without the optimizations.
No breaking changes. No migration headaches.
The Developer Experience
One of the most exciting aspects is the DX improvement.
Imagine:
✓ No more dependency arrays
✓ No more “why is this re-rendering?” debugging sessions
✓ No more premature optimization debates in PRs
✓ No more useMemo/useCallback fatigue
You write code naturally, declaratively, functionally, without worrying about performance traps. The compiler handles the rest.
You should be able to write React like it’s 2015 — and still get 2024 performance.
First: Check Your App’s Health (Don’t Skip This!)
Before you install the compiler — run a compatibility check. The React Compiler only works if your components follow React’s “Rules of React”, immutability, no side effects in render, etc.
If your code violates these rules, the compiler will silently skip optimization — and you’ll have no idea why your app still re-renders like crazy.
How to Check Compatibility
Step 1: Use Health Check package from react team
First Check how compatible your project is with the React Compiler.
npx react-compiler-health-check
This will generally verify if your app is following the rules of React and will highlight any issues
Step 2: Use the Official React Compiler ESLint Plugin (Recommended)
Install the plugin:
npm install eslint-plugin-react-compiler --save-dev
Add it to your .eslintrc:
{
"plugins": ["react-compiler"],
"rules": {
"react-compiler/react-compiler": "error"
}
}
Then run:
npx eslint .
It will flag components that are unsafe for compilation — e.g.:
Mutating refs during render
Using
evalor dynamic keysBreaking hook rules
Passing unstable props via spread (
{...props})
Fix these first. The compiler can’t optimize around them. You don’t need to rewrite your app, but you do need to understand what breaks the compiler’s assumptions.
Compatibility ≠ Optimization. Even “100% compatible” code may not be optimized unless you adjust patterns like useMutation, dynamic keys, or inline JSX children.
How to Try It Today (Step-by-Step)
Step 1: Install the Experimental Build
You need React 19 experimental builds. In your project:
npm install react@experimental react-dom@experimental
The compiler is opt-in and only works with the experimental channel for now.
Step 2: Enable the Compiler
If you’re using Vite + TypeScript
Install the Babel plugin:
npm install babel-plugin-react-compiler --save-dev
Then, in your babel.config.js:
module.exports = {
plugins: [
["babel-plugin-react-compiler", {
compilationMode: "infer"
}]
]
};
compilationMode: "infer" tells the compiler to auto-detect which components are safe to optimize.
If You’re Using Next.js (Recommended Setup):
Next.js includes built-in, optimized support for the React Compiler — using SWC under the hood for faster builds.
Install the babel plugin:
npm install babel-plugin-react-compiler
Then, in your next.config.js:
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
reactCompiler: true,
},
}
export default nextConfig
Next.js only compiles files that actually use JSX or React Hooks — so builds stay fast.
For Expo, you need to install the babel plugin (not needed for SDK54 and above)
npx expo install babel-plugin-react-compiler@beta
Then update App.json
{
"expo": {
"experiments": {
"reactCompiler": true
}
}
}
Then update your eslint, first install the react compiler plugin
npx expo install eslint-plugin-react-compiler -D
Then update the eslint configuration
// <https://docs.expo.dev/guides/using-eslint/>
const { defineConfig } = require('eslint/config');
const expoConfig = require('eslint-config-expo/flat');
const reactCompiler = require('eslint-plugin-react-compiler');
module.exports = defineConfig([
expoConfig,
reactCompiler.configs.recommended,
{
ignores: ['dist/*'],
},
]);
Optional: Opt-In Mode
reactCompiler: {
compilationMode: 'annotation',
},
}
Then, in any component:
export default function MyComponent() {
"use memo"
// ... your code
}
Or to opt-out:
"use no memo"
Step 3: Write “Naive” Code — Let the Compiler Do the Work
Take a look at this component written without any useMemo or useCallback:
// Before: “Naive” version — no manual memoization
function Counter() {
const [counter, setCounter] = useState(0);
const [name, setName] = useState("Mohamed");
const handleButtonClick = () => {
setCounter(value => value + 1);
};
const welcomeMessage = Welcome, Mr. ${name}!; // This gets auto-memoized!
return (
<div>
<h1>{welcomeMessage}</h1>
<p>Counter value: {counter}</p>
<button onClick={handleButtonClick}>Increment</button>
<input
value={name}
onChange={event => setName(event.target.value)}
/>
</div>
);
}
When you edit the input (name), the welcome string and handleButtonClick function are automatically memoized by the compiler — even though you didn’t write useMemo or useCallback.
You can verify this in React DevTools → “Highlight updates when components render.” You’ll see only the necessary parts re-render.
Step 4: Check Compiler Output (Optional — for the Curious)
The compiler rewrites your code behind the scenes. You can see the output by:
Running your dev server
Opening DevTools → Sources
Looking for
.hot-update.jsor compiled component files
You’ll see generated code like:
const $ = useMemo(() => Welcome, Mr. ${name}!, [name]);
const handleButtonClick = useCallback(() => { ... }, []);
inserted automatically. You didn’t write it. The compiler did.
Gotchas & Limitations
These limitations are based on real-world testing (see: I Tried React Compiler ) and React Summit 2024 demos. Even if your app is “compatible,” the compiler may not optimize everything unless you adjust patterns like useMutation, dynamic keys, or inline JSX children.
✗ The compiler can’t optimize components that use eval, new Function, or highly dynamic patterns.
✗ Avoid useRef mutations in render — the compiler can’t track those.
✗ Spread props ({...props}) may limit optimization — prefer explicit props when possible.
✗ Libraries returning non-memoized objects from hooks (e.g., react-query’s useMutation) can break memoization. Extract .mutate or .data before passing to callbacks.
✓ Safe patterns: useState, useEffect, simple derived values, inline functions — all optimized automatically.
If the compiler can’t optimize it, it leaves your code as-is. No breakage. Just missed opportunities.
I'm Already Using It in Production
I don't wait for "stable" to test real tools.
I've implemented the React Compiler in two production apps:
1. Open Tarteel — Next.js Web App
A Quran recitation and correction platform built with Next.js App Router.
Compiler enabled via
next.config.jsESLint plugin running in CI
2. Open Mushaf Native — Expo (React Native) App
A mobile Quran reader.
Compiler enabled via Babel plugin in
babel.config.jsRunning on Android in internal builds and in production
Zero crashes. Zero state bugs.
Yes, the React Compiler works in React Native. It doesn't care if you're rendering to DOM or native views. If it's JSX and hooks, it optimizes it.
Tip: In Expo, make sure you're on SDK 49+ and React 18.3+ experimental. Clear your Metro cache after enabling: npx expo start -c
This isn't a lab experiment. This is live, user-facing code.
The compiler isn't magic, but it's real. When paired with a health check and minor pattern tweaks, it delivers results.
You don't need permission to try it.
Just don't skip the audit.
Final Thoughts
The React Compiler isn’t just an incremental improvement. It’s a paradigm shift.
It takes one of React’s biggest pain points, performance optimization — and automates it. Completely.
20% faster apps. Zero extra code. Zero extra effort.
If that doesn’t get you excited, you’re not paying attention.
But, and this is critical, it’s not magic. It’s math. And math needs clean inputs.
Run the health check. Fix the gotchas. Then let the compiler fly.
Attribution: cover photo from ClickerHappy



