Don't optimize blindly. Measure first, then apply targeted fixes. Here's the systematic approach we use at Google.
Step 1: Measure
// React DevTools Profiler
// 1. Open React DevTools → Profiler tab
// 2. Click Record
// 3. Interact with your app
// 4. Stop recording
// 5. Look for components with long render timesStep 2: Identify the Problem
Unnecessary Re-renders
// Problem: Parent re-render causes child re-render
function Parent() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(c => c + 1)}>{count}</button>
<ExpensiveChild /> {/* Re-renders even though it doesn't use count */}
</div>
);
}
// Fix: React.memo
const ExpensiveChild = React.memo(function ExpensiveChild() {
return <div>{/* expensive render */}</div>;
});Expensive Computations
// Problem: filtering runs on every render
function UserList({ users, query }) {
const filtered = users.filter(u => u.name.includes(query)); // Runs every render!
return filtered.map(u => <UserCard key={u.id} user={u} />);
}
// Fix: useMemo
function UserList({ users, query }) {
const filtered = useMemo(
() => users.filter(u => u.name.includes(query)),
[users, query]
);
return filtered.map(u => <UserCard key={u.id} user={u} />);
}Callback Identity
// Problem: new function reference every render breaks memo
function Parent() {
const handleClick = (id) => deleteItem(id); // New ref every render!
return <MemoizedList onClick={handleClick} />;
}
// Fix: useCallback
function Parent() {
const handleClick = useCallback((id) => deleteItem(id), []);
return <MemoizedList onClick={handleClick} />;
}Step 3: Apply Targeted Fixes
- Virtualization: For long lists (1000+ items), use react-window or @tanstack/virtual
- Code splitting: Lazy load routes and heavy components
- Debounce inputs: Don't trigger re-renders on every keystroke
- State colocation: Move state closer to where it's used
- Context splitting: Split contexts to prevent unnecessary consumer re-renders
What NOT to Optimize
- Don't memo everything — memo has a cost too
- Don't optimize before measuring
- Don't optimize components that render rarely
- Don't optimize if users aren't experiencing issues