With React 19.2, the React Compiler reached 1.0 and is now stable. This is one of the most impactful changes to React's developer experience since hooks: the compiler automatically analyzes your components and adds memoization where it will help, eliminating the need for manual useMemo, useCallback, and memo in most cases.
At Meta, the compiler has been running in production across Facebook and Instagram, where it delivered up to 12% faster initial loads and over 2.5x faster interactions in the Meta Quest Store. Those are significant numbers at scale.
What the Compiler Does
React's rendering model re-runs your component function every time state or props change. The component returns JSX, React diffs it against the previous output, and updates the DOM accordingly. The problem is that re-running the function also re-creates every object, array, and callback inside it — even when the inputs haven't changed.
Developers have traditionally solved this with manual memoization:
Manual memoization (pre-compiler)
const ExpensiveList = memo(function ExpensiveList({ items, filter }: Props) {
const filtered = useMemo(
() => items.filter(item => item.category === filter),
[items, filter]
);
const handleClick = useCallback(
(id: string) => { /* ... */ },
[]
);
return filtered.map(item => (
<ListItem key={item.id} item={item} onClick={handleClick} />
));
});
This works, but it's tedious, error-prone (wrong dependency arrays cause stale data), and clutters your code with optimization plumbing. The React Compiler does this automatically at build time.
How It Works
The compiler is a build-time tool — it runs as a Babel or SWC plugin during your build step. It analyzes each component's data flow, identifies which values depend on which inputs, and inserts memoization code automatically. The key insight is that it can see the entire component at once, including conditional branches, which means it can memoize more aggressively and accurately than a developer manually adding useMemo calls.
When the compiler processes a component, it:
- Builds a dependency graph of all values in the component
- Identifies which values are "reactive" (depend on props, state, or context)
- Wraps computed values in memoization that tracks only their actual dependencies
- Memoizes JSX output so child components skip re-rendering when their props haven't changed
The result is code that performs as if you manually memoized everything perfectly — which is better than what most developers achieve in practice.
What This Means for Your Code
The practical advice is straightforward:
- Stop adding new
useMemo/useCallback/memocalls. The compiler handles this better than you can manually. - Don't rush to remove existing ones. They're harmless — the compiler recognizes them and works alongside them. Remove them gradually during normal refactoring.
- Follow the Rules of React. The compiler assumes your components are pure functions of their props and state. If you're mutating objects, reading from external mutable stores without
useSyncExternalStore, or doing other things that violate React's rules, the compiler may produce incorrect optimizations.
Enabling the Compiler
For new projects, the compiler is on by default with the latest project templates (Expo, Vite, Next.js). For existing projects:
next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
experimental: {
reactCompiler: true,
},
};
export default nextConfig;
vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
const ReactCompilerConfig = { /* optional config */ };
export default defineConfig({
plugins: [
react({
babel: {
plugins: [["babel-plugin-react-compiler", ReactCompilerConfig]],
},
}),
],
});
Pro Tip: The compiler ships ESLint rules (via
eslint-plugin-react-hooks) that flag code patterns the compiler can't safely optimize. Run these in CI to catch issues early.
Should You Adopt It Now?
Yes. The compiler is stable, battle-tested at Meta scale, and the performance improvements are free. The main risk is code that violates React's rules (mutating props, side effects during render), and the ESLint plugin will flag those for you. For any project on React 19.2 or later, there's no reason not to enable it.
