|$ curl https://forge-ai.dev/api/markdown?path=docs/react/compiler
$cat docs/react-—-react-compiler.md
updated Last week·14 min read·published

React — React Compiler

ReactIntermediate🎯Free Tools
Introduction

The React Compiler (formerly React Forget) is an ahead-of-time compiler that automatically optimizes React components. It analyzes your code and inserts memoization where beneficial, eliminating the need for manual useMemo, useCallback, and React.memo calls.

The compiler works at build time, transforming your source code into optimized versions that skip unnecessary re-renders. It is a Babel/SWC plugin that runs during compilation — no runtime changes are needed.

Setup & Configuration
terminal
Bash
1# Install the React Compiler (React 19+)
2npm install babel-plugin-react-compiler
3
4# Or with the official React Compiler package
5npm install react-compiler
6
7# For Vite projects — add to vite.config.ts
8npm install babel-plugin-react-compiler vite-plugin-react-compiler
9
10# For Next.js — add to next.config.js
11npm install babel-plugin-react-compiler
tsconfig.json
JSON
1{
2 "compilerOptions": {
3 "target": "ES2022",
4 "jsx": "react-jsx",
5 "module": "NodeNext",
6 "moduleResolution": "NodeNext"
7 }
8}
config.ts
JavaScript
1// Vite configuration — vite.config.ts
2import { defineConfig } from "vite";
3import react from "@vitejs/plugin-react";
4
5export default defineConfig({
6 plugins: [
7 react({
8 babel: {
9 plugins: [
10 ["babel-plugin-react-compiler", {
11 target: "19", // React 19
12 }],
13 ],
14 },
15 }),
16 ],
17});
18
19// Next.js configuration — next.config.js
20/** @type {import('next').NextConfig} */
21const nextConfig = {
22 experimental: {
23 reactCompiler: true,
24 },
25};
26
27module.exports = nextConfig;

info

The React Compiler requires React 19 or later. It is backward compatible — your existing code continues to work unchanged. The compiler optimizes automatically without any code modifications.
Automatic Memoization
auto_memo.tsx
TSX
1// BEFORE — manual memoization (React 18 style)
2import { useMemo, useCallback, memo } from "react";
3
4function ProductList({ products, onSelect }: Props) {
5 const sortedProducts = useMemo(
6 () => [...products].sort((a, b) => a.price - b.price),
7 [products]
8 );
9
10 const handleClick = useCallback(
11 (id: string) => {
12 onSelect(id);
13 },
14 [onSelect]
15 );
16
17 return (
18 <ul>
19 {sortedProducts.map((product) => (
20 <ProductItem
21 key={product.id}
22 product={product}
23 onClick={handleClick}
24 />
25 ))}
26 </ul>
27 );
28}
29
30const ProductItem = memo(function ProductItem({ product, onClick }: Props) {
31 return (
32 <li onClick={() => onClick(product.id)}>
33 {product.name} — ${product.price}
34 </li>
35 );
36});
37
38// AFTER — React Compiler handles it automatically
39function ProductList({ products, onSelect }: Props) {
40 // Compiler automatically memoizes this computation
41 const sortedProducts = [...products].sort((a, b) => a.price - b.price);
42
43 // Compiler automatically memoizes this callback
44 const handleClick = (id: string) => {
45 onSelect(id);
46 };
47
48 return (
49 <ul>
50 {sortedProducts.map((product) => (
51 <ProductItem
52 key={product.id}
53 product={product}
54 onClick={handleClick}
55 />
56 ))}
57 </ul>
58 );
59}
60
61// No memo() needed — the compiler tracks which props change
62function ProductItem({ product, onClick }: Props) {
63 return (
64 <li onClick={() => onClick(product.id)}>
65 {product.name} — ${product.price}
66 </li>
67 );
68}

best practice

With the React Compiler, you can remove manual useMemo, useCallback, and memo() calls. The compiler is smarter about when to memoize based on actual usage patterns.
Dependency Tracking
dependency.tsx
TSX
1// The compiler statically analyzes your code to determine dependencies
2
3// BEFORE — manual dependency array (error-prone)
4function UserProfile({ userId }: { userId: string }) {
5 const [user, setUser] = useState(null);
6
7 useEffect(() => {
8 fetchUser(userId).then(setUser);
9 }, [userId]); // Must remember to include userId
10
11 const fullName = useMemo(
12 () => user ? `${user.firstName} ${user.lastName}` : "",
13 [user] // Must remember to include user
14 );
15
16 const handleUpdate = useCallback(
17 (data: Partial<User>) => {
18 setUser((prev) => ({ ...prev, ...data }));
19 },
20 [] // Empty — no dependencies needed
21 );
22}
23
24// AFTER — compiler tracks dependencies automatically
25function UserProfile({ userId }: { userId: string }) {
26 const [user, setUser] = useState(null);
27
28 useEffect(() => {
29 fetchUser(userId).then(setUser);
30 }, [userId]); // Compiler verifies this is correct
31
32 const fullName = user
33 ? `${user.firstName} ${user.lastName}`
34 : "";
35 // Compiler memoizes based on actual user access
36
37 const handleUpdate = (data: Partial<User>) => {
38 setUser((prev) => ({ ...prev, ...data }));
39 };
40 // Compiler detects no external dependencies — runs once
41}
42
43// The compiler understands:
44// - Variable reads (local and prop references)
45// - Object property access
46// - Array methods
47// - Closures
48// - React hooks and their dependency rules
49
50// Example: compiler tracks which properties are accessed
51function ProductCard({ product }: { product: Product }) {
52 // Only depends on product.name and product.price
53 // If product.description changes, this doesn't re-render
54 return (
55 <div>
56 <h3>{product.name}</h3>
57 <p>${product.price}</p>
58 </div>
59 );
60}
Compiler Directives
directives.tsx
TSX
1// The compiler provides directives for fine-grained control
2
3// "use no memo" — opt out of compiler optimization for a component
4function ExperimentalComponent() {
5 "use no memo";
6
7 // This component will NOT be optimized by the compiler
8 // Useful for components with intentional re-render patterns
9 return <div>Not memoized</div>;
10}
11
12// "use no memo" on a function expression
13const ManualComponent = function () {
14 "use no memo";
15
16 const [count, setCount] = useState(0);
17 return <button onClick={() => setCount((c) => c + 1)}>{count}</button>;
18};
19
20// Compiler handles these patterns automatically:
21// 1. Inline object literals — memoized when passed as props
22function Parent() {
23 // This object is memoized — doesn't cause child re-renders
24 return <Child style={{ color: "red", fontSize: 16 }} />;
25}
26
27// 2. Inline function expressions — memoized for callbacks
28function Parent() {
29 // This callback is memoized — stable reference
30 return <Child onClick={() => console.log("clicked")} />;
31}
32
33// 3. Conditional hooks — the compiler validates hook rules
34function Component({ showDetails }: { showDetails: boolean }) {
35 const [name, setName] = useState("");
36 // The compiler ensures hooks are always called in the same order
37
38 if (showDetails) {
39 const [details, setDetails] = useState(null);
40 return <Details data={details} />;
41 }
42
43 return <p>{name}</p>;
44}
45
46// Disabling compiler for specific files
47// Add // @react-compiler-ignore at the top of a file
48// Or in your compiler config:
49// { "sources": { "path/to/file.tsx": { "compilationMode": "skip" } } }

warning

Only use "use no memo" when you have a specific reason to disable optimization. The compiler handles the vast majority of cases correctly and produces better memoization than manual approaches.
Compatibility & Migration
compatibility.tsx
TSX
1// The compiler is fully backward compatible
2
3// All these patterns continue to work:
4import { useState, useEffect, useMemo, useCallback, memo } from "react";
5
6// Manual memoization — still works, but unnecessary
7const MemoizedComponent = memo(function MyComponent({ data }: Props) {
8 const processed = useMemo(() => data.map(transform), [data]);
9 const handleClick = useCallback(() => {}, []);
10 return <div onClick={handleClick}>{processed}</div>;
11});
12
13// Migration steps:
14// 1. Add the compiler plugin to your build config
15// 2. Run your test suite — everything should pass
16// 3. Optionally remove manual memoization calls
17// 4. Verify bundle size decreased
18
19// What the compiler handles:
20// ✅ useState, useEffect, useRef, useContext — standard hooks
21// ✅ useMemo, useCallback — can be removed after enabling
22// ✅ React.memo() — can be removed after enabling
23// ✅ Custom hooks — compiler analyzes hook internals
24// ✅ Conditional rendering — tracks dependencies per branch
25// ✅ List rendering — memoizes individual list items
26// ✅ Context consumers — avoids unnecessary re-renders
27
28// What the compiler does NOT do:
29// ❌ Optimize effects (useEffect, useLayoutEffect)
30// ❌ Change runtime behavior — same React semantics
31// ❌ Optimize external library calls
32// ❌ Fix broken code — you must follow React rules
33
34// Framework support:
35// - Next.js (experimental.reactCompiler: true in next.config.js)
36// - Vite (via babel-plugin-react-compiler)
37// - Parcel (via babel-plugin-react-compiler)
38// - Webpack (via babel-loader with the plugin)
39// - Expo (via babel config)
Best Practices

1. Enable the compiler first, then progressively remove manual useMemo, useCallback, and memo() calls.

2. Keep following the Rules of Hooks — the compiler optimizes within these rules, it does not replace them.

3. Use "use no memo" only for components with intentional re-render patterns (like animations or real-time data).

4. Test your application after enabling the compiler — the optimization should be transparent but always verify.

5. The compiler works best with React 19+ and modern bundlers (Vite, Next.js, etc.).

6. Don't rely on compiler internals — write clean React code and let the compiler optimize it.

$Blueprint — Engineering Documentation·Section ID: REACT-CMP·Revision: 1.0