React Compiler
HardThe React Compiler (formerly React Forget) is a build-time tool that automatically memoizes React components and hooks. It analyzes your code's data flow using static analysis and inserts optimal caching, eliminating the need for manual useMemo, useCallback, and React.memo. The compiler understands the Rules of React and optimizes only code that follows them.
Interactive Visualization
Key Points
- Runs at build time — analyzes data dependencies via static analysis and inserts fine-grained caching
- Replaces manual useMemo, useCallback, and React.memo with automatic equivalents
- Requires components to follow the Rules of React: pure render, immutable props/state, unconditional hooks
- Uses an internal useMemoCache hook with per-expression cache slots, more granular than manual useMemo blocks
- The "use no memo" directive opts individual components out when manual control is needed
Code Examples
Before: Manual Memoization
import { useMemo, useCallback, memo, useState } from 'react' interface Item { id: string; name: string; price: number } const ItemCard = memo(function ItemCard({ item, onSelect, }: { item: Item onSelect: (id: string) => void }) { return ( <div onClick={() => onSelect(item.id)}> <h3>{item.name}</h3> <p>${item.price}</p> </div> ) }) function ItemList({ items }: { items: Item[] }) { const [query, setQuery] = useState('') const filtered = useMemo( () => items.filter(i => i.name.includes(query)), [items, query] ) const handleSelect = useCallback((id: string) => { console.log('Selected:', id) }, []) return ( <div> <input value={query} onChange={e => setQuery(e.target.value)} /> {filtered.map(item => ( <ItemCard key={item.id} item={item} onSelect={handleSelect} /> ))} </div> ) }
Without the compiler, you must manually wrap derived data in useMemo, callbacks in useCallback, and child components in memo. Missing any part of this chain defeats the optimization. Dependency arrays must be manually maintained and are a common source of bugs.
After: React Compiler Handles It
import { useState } from 'react' interface Item { id: string; name: string; price: number } function ItemCard({ item, onSelect, }: { item: Item onSelect: (id: string) => void }) { return ( <div onClick={() => onSelect(item.id)}> <h3>{item.name}</h3> <p>${item.price}</p> </div> ) } function ItemList({ items }: { items: Item[] }) { const [query, setQuery] = useState('') const filtered = items.filter(i => i.name.includes(query)) const handleSelect = (id: string) => { console.log('Selected:', id) } return ( <div> <input value={query} onChange={e => setQuery(e.target.value)} /> {filtered.map(item => ( <ItemCard key={item.id} item={item} onSelect={handleSelect} /> ))} </div> ) } // The compiler automatically: // 1. Memoizes the filtered array (like useMemo) // 2. Memoizes handleSelect (like useCallback) // 3. Memoizes ItemCard renders (like React.memo) // No manual optimization code needed!
With the React Compiler, you write plain idiomatic React. The compiler analyzes data flow at build time and inserts caching with per-expression granularity. It produces the same optimized output as perfect manual memoization, but with zero developer effort and zero risk of stale dependency bugs.
Common Mistakes
- Expecting the compiler to optimize code that mutates variables during render — mutations break the purity assumption
- Reading from mutable refs during render (not in effects) which makes the output unpredictable to the compiler
- Assuming the compiler replaces all performance optimization — it handles memoization but not algorithmic improvements or virtualization
Interview Tips
- Explain that the compiler is a build-time static analysis tool, not a runtime library
- Know what the compiler replaces (useMemo, useCallback, memo) and what it does not (useEffect, algorithmic optimization)
- Discuss the Rules of React that enable compilation: purity, immutability, unconditional hooks