Skip to main content
Practice Problems

React performance optimization techniques

Performance Optimization in React

React is fast by default, but large apps can suffer from unnecessary re-renders, heavy computations, and large bundle sizes. Here are the key techniques to optimize React performance.


1. React.memo โ€” Prevent Unnecessary Re-renders

tsx
// Re-renders ONLY when props change (shallow comparison) const ExpensiveList = React.memo(function ExpensiveList({ items }: { items: Item[] }) { return ( <ul> {items.map(item => <li key={item.id}>{item.name}</li>)} </ul> ); });

2. useMemo โ€” Cache Expensive Computations

tsx
function Dashboard({ transactions }: { transactions: Transaction[] }) { // Only recalculated when transactions change const stats = useMemo(() => { return { total: transactions.reduce((sum, t) => sum + t.amount, 0), average: transactions.reduce((sum, t) => sum + t.amount, 0) / transactions.length, max: Math.max(...transactions.map(t => t.amount)), }; }, [transactions]); return <StatsCard stats={stats} />; }

3. useCallback โ€” Stable Function References

tsx
function Parent() { const [count, setCount] = useState(0); // Without useCallback: new function every render โ†’ Child re-renders // With useCallback: same function reference โ†’ Child skips re-render const handleClick = useCallback(() => { console.log("clicked"); }, []); // Stable reference return ( <div> <span>{count}</span> <button onClick={() => setCount(c => c + 1)}>+</button> <MemoizedChild onClick={handleClick} /> </div> ); } const MemoizedChild = React.memo(function Child({ onClick }: { onClick: () => void }) { console.log("Child rendered"); return <button onClick={onClick}>Click me</button>; });

4. Code Splitting with React.lazy

tsx
import { lazy, Suspense } from "react"; // Component is loaded only when needed const HeavyChart = lazy(() => import("./HeavyChart")); const AdminPanel = lazy(() => import("./AdminPanel")); function App() { return ( <Suspense fallback={<Loading />}> <Routes> <Route path="/dashboard" element={<HeavyChart />} /> <Route path="/admin" element={<AdminPanel />} /> </Routes> </Suspense> ); }

5. Virtualization for Large Lists

tsx
import { useVirtualizer } from "@tanstack/react-virtual"; function VirtualList({ items }: { items: Item[] }) { const parentRef = useRef<HTMLDivElement>(null); const virtualizer = useVirtualizer({ count: items.length, // 10,000+ items getScrollElement: () => parentRef.current, estimateSize: () => 50, // Row height }); return ( <div ref={parentRef} style={{ height: "400px", overflow: "auto" }}> <div style={{ height: virtualizer.getTotalSize() }}> {virtualizer.getVirtualItems().map(virtualRow => ( <div key={virtualRow.key} style={{ position: "absolute", top: virtualRow.start, height: virtualRow.size, }} > {items[virtualRow.index].name} </div> ))} </div> </div> ); }

6. Avoid Inline Objects and Functions

tsx
// โŒ New object every render โ†’ child re-renders <Child style={{ color: "red" }} /> <Child onClick={() => console.log("click")} /> // โœ… Stable references const style = useMemo(() => ({ color: "red" }), []); const handleClick = useCallback(() => console.log("click"), []); <Child style={style} /> <Child onClick={handleClick} />

7. State Colocation

Keep state as close as possible to where it's used:

tsx
// โŒ State too high โ€” entire app re-renders on hover function App() { const [hoveredId, setHoveredId] = useState<string | null>(null); return <List items={items} hoveredId={hoveredId} onHover={setHoveredId} />; } // โœ… State colocated โ€” only ListItem re-renders function ListItem({ item }: { item: Item }) { const [isHovered, setIsHovered] = useState(false); return ( <div onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} className={isHovered ? "highlighted" : ""} > {item.name} </div> ); }

Quick Reference

ProblemSolution
Child re-renders when parent re-rendersReact.memo
Expensive calculation runs every renderuseMemo
Callback prop causes re-renderuseCallback + React.memo
Large initial bundleCode splitting (React.lazy)
Rendering 10,000+ itemsVirtualization
State changes cause widespread re-rendersState colocation
Heavy non-urgent updatesuseTransition / useDeferredValue

Important:

Don't optimize prematurely โ€” React is fast by default. Profile first with React DevTools Profiler, identify actual bottlenecks, then apply targeted optimizations. The biggest wins usually come from state colocation, code splitting, and virtualization rather than memo/useMemo everywhere.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems