React Render Optimization
Modern render performance patterns for React 19+.
Decision Tree: React Compiler First (2026)
Is React Compiler enabled? ├─ YES → Let compiler handle memoization automatically │ Only use useMemo/useCallback as escape hatches │ DevTools shows "Memo ✨" badge │ └─ NO → Profile first, then optimize 1. React DevTools Profiler 2. Identify actual bottlenecks 3. Apply targeted optimizations
React Compiler (Primary Approach)
React 19's compiler automatically memoizes:
-
Component re-renders
-
Intermediate values (like useMemo)
-
Callback references (like useCallback)
-
JSX elements
// next.config.js (Next.js 16+) const nextConfig = { reactCompiler: true, }
// Expo SDK 54+ enables by default
Verification: Open React DevTools → Look for "Memo ✨" badge
When Manual Memoization Still Needed
Use useMemo /useCallback as escape hatches when:
// 1. Effect dependencies that shouldn't trigger re-runs const stableConfig = useMemo(() => ({ apiUrl: process.env.API_URL }), [])
useEffect(() => { initializeSDK(stableConfig) }, [stableConfig])
// 2. Third-party libraries without compiler support const memoizedValue = useMemo(() => expensiveThirdPartyComputation(data), [data])
// 3. Precise control over memoization boundaries const handleClick = useCallback(() => { // Critical callback that must be stable }, [dependency])
Virtualization Thresholds
Item Count Recommendation
< 100 Regular rendering usually fine
100-500 Consider virtualization
500+ Virtualization required
import { useVirtualizer } from '@tanstack/react-virtual'
const virtualizer = useVirtualizer({ count: items.length, getScrollElement: () => parentRef.current, estimateSize: () => 50, overscan: 5, })
State Colocation
Move state as close to where it's used as possible:
// ❌ State too high - causes unnecessary re-renders function App() { const [filter, setFilter] = useState('') return ( <Header /> {/* Re-renders on filter change! */} <FilterInput value={filter} onChange={setFilter} /> <List filter={filter} /> ) }
// ✅ State colocated - minimal re-renders function App() { return ( <Header /> <FilterableList /> {/* State inside */} ) }
Profiling Workflow
-
React DevTools Profiler: Record, interact, analyze
-
Identify: Components with high render counts or duration
-
Verify: Is the re-render actually causing perf issues?
-
Fix: Apply targeted optimization
-
Measure: Confirm improvement
Quick Wins
-
Key prop: Stable, unique keys for lists
-
Lazy loading: React.lazy() for code splitting
-
Debounce: Input handlers with useDeferredValue
-
Suspense: Streaming with proper boundaries
Key Decisions
Decision Recommendation
Memoization Let React Compiler handle it (2026 default)
Lists 100+ items Use TanStack Virtual
State placement Colocate as close to usage as possible
Profiling Always measure before optimizing
Related Skills
-
react-server-components-framework
-
Server-first rendering
-
vite-advanced
-
Build optimization
-
e2e-testing
-
Performance testing with Playwright
References
-
React Compiler Migration - Compiler adoption
-
Memoization Escape Hatches - When useMemo needed
-
TanStack Virtual - Virtualization
-
State Colocation - State placement
-
DevTools Profiler - Finding bottlenecks