Use when optimizing render performance, profiling components, reducing bundle size, or implementing code splitting. Prevents the common mistake of premature optimization or missing obvious re-render bottlenecks. Covers React.memo, useMemo, useCallback, React Compiler (React 19), Profiler, React.lazy, bundle analysis, virtualization, image optimization. Keywords: React.memo, useMemo, useCallback, code splitting, Profiler, lazy, bundle too big, app is slow, too many re-renders, laggy, optimize, reduce bundle size..
license
MIT
compatibility
Designed for Claude Code. Requires React 18.x or 19.x with TypeScript.
metadata
{"author":"OpenAEC-Foundation","version":"1.0"}
react-impl-performance
Quick Reference
Performance Optimization Tools
Tool
Purpose
React Version
When to Use
React.memo
Skip re-renders when props unchanged
18 + 19
Expensive component, same props frequently
useMemo
Cache expensive computation results
18 + 19
Calculation >1ms on target hardware
useCallback
Stable function reference for children
18 + 19
Function prop to memo-wrapped child
React Compiler
Automatic memoization at build time
19+
Replaces manual memo/useMemo/useCallback
<Profiler>
Measure render durations
18 + 19
Identify slow components
React DevTools
Visual profiling (flamegraph, ranked)
18 + 19
Interactive performance investigation
React.lazy
Code splitting per route/component
18 + 19
Reduce initial bundle size
@tanstack/virtual
Virtualize long lists
18 + 19
Lists with 1000+ items
Critical Warnings
NEVER optimize before measuring. ALWAYS use the Profiler component or React DevTools to identify the actual bottleneck first. Premature optimization adds complexity without measurable benefit.
NEVER wrap every component in React.memo. The shallow comparison itself has a cost. ONLY use memo when a component re-renders frequently with the same props AND rendering is noticeably slow.
NEVER use JSON.stringify in a custom arePropsEqual function for React.memo. This is slower than just re-rendering the component.
NEVER rely on useMemo as a semantic guarantee. React MAY discard cached values (on suspend, during development). Use useRef if you need a persistent reference.
Decision Tree: When to Optimize
Component renders slowly?
├── NO → STOP. Do not optimize.
└── YES → Measure with Profiler/DevTools
├── Re-renders with same props?
│ ├── Props are primitives → Use React.memo
│ ├── Props include objects → useMemo the object, then React.memo
│ └── Props include functions → useCallback the function, then React.memo
├── Expensive computation during render?
│ └── Use useMemo with dependency array
├── Large bundle size / slow initial load?
│ ├── Route-based → React.lazy + Suspense
│ └── Feature-based → Dynamic import + React.lazy
├── Long list (1000+ items)?
│ └── Use @tanstack/virtual
└── Using React 19?
└── Enable React Compiler → removes need for manual memo/useMemo/useCallback
React.memo
Wraps a component to skip re-rendering when props have not changed (shallow Object.is comparison per prop).
ALWAYS ensure props passed to a memo component are referentially stable. Passing a new object or function literal on every render defeats memo entirely.
memo does NOT prevent re-renders caused by:
Internal state changes (useState, useReducer)
Context value changes (useContext)
useMemo
Caches the result of an expensive calculation between re-renders.
ALWAYS include every reactive value used inside the calculation in the dependency array. NEVER omit the dependency array — this recalculates every render, defeating the purpose.
useCallback
Returns a stable function reference between re-renders. Equivalent to useMemo(() => fn, deps).
ALWAYS use updater functions to remove state from the dependency array:
// WRONG: todos changes every update, useCallback is uselessconst handleAdd = useCallback((text: string) => {
setTodos([...todos, { id: nextId++, text }]);
}, [todos]);
// CORRECT: updater removes todos dependencyconst handleAdd = useCallback((text: string) => {
setTodos((prev) => [...prev, { id: nextId++, text }]);
}, []);
React Compiler (React 19+)
The React Compiler automatically applies memoization at build time, replacing manual memo, useMemo, and useCallback. When enabled, you do NOT need to write these manually.