Skip to content

React Compiler Explained — Auto-Memoization with React Forget

DodaTech 6 min read

In this tutorial, you'll learn about React Compiler Explained. We cover key concepts, practical examples, and best practices.

The React Compiler automatically memoizes React components and hooks at build time, eliminating the need for manual useMemo, useCallback, and React.memo while preserving identical runtime behavior.

What You'll Learn

Understand how the React Compiler (React Forget) analyzes your code at compile time to add automatic memoization, reducing re-renders without manual optimization and simplifying your React codebase.

Why It Matters

Manual memoization clutters code with useMemo, useCallback, and React.memo wrappers. A study of large React apps shows 30-50% of performance bugs come from incorrect or missing memoization. The React Compiler automates this — your code is cleaner and correctly optimized by default.

Real-World Use

A large settings dashboard with 40+ config options re-renders the entire tree whenever one toggle changes. Without memoization, it takes 120ms per interaction. With the React Compiler, only the changed toggle re-renders, cutting interaction time to 4ms. Durga Antivirus Pro's settings panel uses this pattern across hundreds of configuration toggles.

How the Compiler Works

The compiler runs as a Babel plugin during build. It analyzes JavaScript/TypeScript and infers which values are "stable" (never change between renders) and which are "reactive" (may change). It then inserts automatic memoization calls:

// Your source code (no manual memoization):
function Profile({ user, posts }) {
  const displayName = user.firstName + " " + user.lastName;
  return (
    <div>
      <h1>{displayName}</h1>
      <PostList items={posts} />
    </div>
  );
}

// What the compiler produces (simplified):
import { c as memoize } from "react-compiler-runtime";

function Profile(t0) {
  const $ = memoize();
  const { user, posts } = t0;
  let displayName;
  if ($[0] !== user) {
    displayName = user.firstName + " " + user.lastName;
    $[0] = user;
    $[1] = displayName;
  } else {
    displayName = $[1];
  }
  let t1;
  if ($[2] !== posts) {
    t1 = <PostList items={posts} />;
    $[2] = posts;
    $[3] = t1;
  } else {
    t1 = $[3];
  }
  return <div><h1>{displayName}</h1>{t1}</div>;
}

Expected output: The compiled version caches displayName and the PostList JSX, reusing them when user and posts have not changed. The source code stays clean while runtime performance matches hand-optimized code.

Rules of the Compiler

The compiler relies on JavaScript rules to determine reactivity:

// This works with the compiler — values used consistently:
function SearchResults({ query, items }) {
  const filtered = items.filter(item =>
    item.title.toLowerCase().includes(query.toLowerCase())
  );
  const total = filtered.length;
  return <div>Found {total} results</div>;
}

// This may confuse the compiler — mutation outside the hook:
const cache = new Map();

function ExpensiveList({ data }) {
  // Mutating external cache — compiler cannot track this
  if (!cache.has(data.id)) {
    cache.set(data.id, processItem(data));
  }
  return <div>{cache.get(data.id)}</div>;
}
// Use refs for mutable values the compiler should not track:
function TimerDisplay() {
  const countRef = useRef(0);

  // Mutating ref.current is fine — refs are excluded from reactivity
  useEffect(() => {
    const id = setInterval(() => countRef.current++, 1000);
    return () => clearInterval(id);
  }, []);

  return <div>Count: {countRef.current}</div>;
}

Expected output: The first component auto-memoizes filtered and total. The second example warns the compiler about untracked external mutation. The third uses useRef for intentional mutable storage.

Enabling the Compiler

Add the Babel plugin to your project:

// babel.config.js (or next.config.js for Next.js)
module.exports = {
  plugins: [
    ["babel-plugin-react-compiler", {
      target: "18", // or "19" for React 19
      compilationMode: "infer",
    }],
  ],
};

// The compiler can be configured per file or globally:
// "annotation": "opt-in" — only compiles files with "use memo" directive
// "compilationMode": "all" — compiles everything (default)
// "panicThreshold": "critical" — only errors on critical issues

Expected output: After adding the plugin, react-compiler-runtime is imported automatically during build. Files compile with automatic memoization. Validate by running the React Compiler Playground in browser to inspect compiled output.

Common Errors

Error: Compilation failed — unexpected mutation of variable

The compiler detected a mutation (like arr.push()) that it cannot safely track. Use immutable patterns: [...arr, newItem] instead of arr.push(newItem). For intentional mutation, mark with "use no memo" directive at the top of the scope.

Error: Hook is conditionally called

The compiler detected a hook inside a conditional or loop. React hooks must be called in the same order every render. Extract the conditional logic into a component that renders the hook unconditionally, or lift the hook above the condition.

Error: Cannot infer reactivity for Module-scoped variable

A module-level variable is being used inside a component. The compiler cannot track mutations across modules. Wrap the value in a useRef or useState to make its reactivity explicit.

Warning: Potential performance cliff — large memoization cache

A component creates large objects or arrays that change every render. The compiler memoizes them, but if they are always new references, memoization adds overhead without benefit. Consider restructuring to minimize allocations.

Error: 'useCallback' cannot be used with compiler memoization

You are mixing manual memoization with the compiler. Remove the manual useMemo/useCallback calls — the compiler handles it. If you must bypass, use "use no memo" directive on that specific hook or component

Practice Questions

  1. What does the React Compiler do at build time? It analyzes component and hook bodies, infers which values are stable vs reactive, and inserts automatic memoization calls to prevent unnecessary re-renders.

  2. How does the compiler handle intentional mutations like event handlers? Event handlers and refs are excluded from reactivity analysis. The compiler recognizes callback patterns and ref mutations as intentionally mutable and does not memoize them.

  3. What is the "use no memo" directive for? It tells the compiler to skip a specific scope (component, hook, or expression). Use it when the compiler's analysis is incorrect or when you need raw performance without memoization overhead.

Challenge

Create a component with 10 derived values computed from props. First, profile its render performance without any memoization. Then enable the React Compiler and compare render times. Next, manually add useMemo and useCallback to the same component and compare bundle size — the compiler version should be smaller and equally fast.

Real-World Task

Take the most frequently re-rendering component in your app (use React DevTools Profiler to find it). Remove all manual useMemo, useCallback, and React.memo calls. Enable the React Compiler. Profile again. Compare bundle size and render performance before and after.

Mini Project: Optimized Settings Page

Build a settings page with:

  • 30+ toggle switches, dropdowns, and text inputs
  • A search bar that filters settings in real time
  • Theme color picker that updates the page live
  • Run the React Compiler and measure interaction latency

Compare three versions: no memoization, hand-optimized with React.memo, and compiler-optimized.

Learning Path

flowchart LR
  A[React Hooks Complete Guide] --> B[React Performance Optimization]
  B --> C[React Compiler Explained]
  C --> D[React Concurrent Features]
  D --> E[React Server Actions]
  style C fill:#4a90d9,color:#fff

Next Steps

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro. Durga Antivirus Pro's configuration panel uses this same auto-memoization approach — 40+ settings toggles re-render independently without any manual React.memo wrappers in the source code.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro