For years, React developers have engaged in a delicate dance with performance optimization. We've wrapped functions in useCallback, computed values in useMemo, and components in memo(). We've debated when these optimizations are necessary and when they're premature.
React 19 changes everything. The React Compiler—previously known as React Forget—automatically memoizes your components and hooks. In most cases, you can delete your manual memoization and let the compiler handle it.
Understanding React Compiler Architecture
The React Compiler is a build-time tool that transforms your React code into an optimized version. It analyzes your component structure and automatically inserts memoization where beneficial:
// Your code
function ProductCard({ product, onAddToCart }) {
const discountedPrice = product.price * (1 - product.discount)
const handleClick = () => onAddToCart(product.id)
return (
<div className="product-card">
<h3>{product.name}</h3>
<p>${discountedPrice.toFixed(2)}</p>
<button onClick={handleClick}>Add to Cart</button>
</div>
)
}
// What the compiler outputs (conceptually)
function ProductCard({ product, onAddToCart }) {
const $ = useMemoCache(4)
let discountedPrice
if ($[0] !== product.price || $[1] !== product.discount) {
discountedPrice = product.price * (1 - product.discount)
$[0] = product.price; $[1] = product.discount; $[2] = discountedPrice
} else {
discountedPrice = $[2]
}
// Similar memoization for handleClick and JSX...
}The Rules of React
The compiler relies on your code following React's rules:
- Components and hooks must be pure—same inputs produce same outputs
- Props and state are immutable—never mutate, always create new objects
- Side effects belong in useEffect—not in render logic
- Hook calls must be at the top level
// ❌ Compiler cannot optimize - mutation
function BadComponent({ items }) {
items.sort() // Mutates the prop!
return <List items={items} />
}
// ✅ Compiler optimizes correctly
function GoodComponent({ items }) {
const sortedItems = [...items].sort() // Creates new array
return <List items={sortedItems} />
}What Gets Auto-Memoized
Automatically Memoized:
- Computed values derived from props or state
- Callback functions and event handlers
- JSX elements when props haven't changed
What Still Needs Attention:
- External dependencies the compiler can't track
- Complex dynamic object creation in render
React Actions: The New Mutation Pattern
function UpdateProfileForm({ userId }) {
const [state, formAction, isPending] = useActionState(
async (prevState, formData) => {
const name = formData.get('name')
const result = await updateProfile(userId, { name })
if (!result.success) return { error: result.error }
return { success: true }
},
null
)
return (
<form action={formAction}>
<input name="name" disabled={isPending} />
{state?.error && <p className="error">{state.error}</p>}
<button disabled={isPending}>{isPending ? 'Saving...' : 'Save'}</button>
</form>
)
}The use() Hook and Async Components
function UserProfile({ userPromise }) {
const user = use(userPromise) // Suspends until resolved
return (<div><h2>{user.name}</h2><p>{user.email}</p></div>)
}
// Unlike useContext, use() can be called conditionally!
function Navigation({ isLoggedIn }) {
if (isLoggedIn) {
const user = use(UserContext)
return <nav>Welcome, {user.name}</nav>
}
return <nav>Please log in</nav>
}Migration Guide
- Update dependencies:
npm install react@19 react-dom@19 - Enable the compiler in next.config.js
- Run
npx react-compiler-healthcheck@latest - Gradually remove manual memoization
- Adopt new patterns (useActionState, useOptimistic, use())
Conclusion
React 19's compiler represents a fundamental shift. The mental overhead of "should I memoize this?" largely disappears. We can write more natural, readable code while getting better performance. The React team has given us permission to write simpler code. Let's use it wisely.



