How to Optimize React App Performance: Tips and Examples
To optimize React app performance, use
React.memo to avoid unnecessary re-renders, useMemo and useCallback to memoize values and functions, and implement React.lazy with Suspense for code splitting. Also, keep components small and avoid heavy computations during rendering.Syntax
React.memo: Wraps a component to memoize it and skip re-render if props don't change.
const MemoComponent = React.memo(Component);
useMemo: Memoizes a computed value to avoid recalculations.
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
useCallback: Memoizes a function to keep the same reference between renders.
const memoizedCallback = useCallback(() => doSomething(a), [a]);
React.lazy and Suspense: Load components lazily to split code.
const LazyComponent = React.lazy(() => import('./LazyComponent'));<Suspense fallback=<div>Loading...</div>><LazyComponent /></Suspense>
jsx
import React, { useMemo, useCallback, Suspense } from 'react'; const MemoComponent = React.memo(function Component({ value, onClick }) { return <div onClick={onClick}>{value}</div>; }); function Example({ a, b, onClick }) { const memoizedValue = useMemo(() => a + b, [a, b]); const memoizedCallback = useCallback(() => onClick(memoizedValue), [onClick, memoizedValue]); return <MemoComponent value={memoizedValue} onClick={memoizedCallback} />; } const LazyComponent = React.lazy(() => import('./LazyComponent')); function App() { return ( <Suspense fallback={<div>Loading...</div>}> <LazyComponent /> </Suspense> ); }
Example
This example shows how to use React.memo and useCallback to prevent unnecessary re-renders of a button component when its parent re-renders.
jsx
import React, { useState, useCallback } from 'react'; const Button = React.memo(({ onClick, children }) => { console.log('Button rendered'); return <button onClick={onClick}>{children}</button>; }); function Counter() { const [count, setCount] = useState(0); const increment = useCallback(() => { setCount(c => c + 1); }, []); return ( <div> <p>Count: {count}</p> <Button onClick={increment}>Increment</Button> </div> ); } export default Counter;
Output
Count: 0
[Button rendered]
// When clicking 'Increment', count updates but 'Button rendered' logs only once because Button does not re-render unnecessarily.
Common Pitfalls
- Not using
React.memoor memo hooks causes components to re-render even when props/state haven't changed. - Overusing
useMemoanduseCallbackcan add complexity without benefit; use them only when expensive calculations or frequent re-renders occur. - Not splitting code with
React.lazycan increase initial load time. - Mutating props or state directly can cause unexpected re-renders.
jsx
import React, { useState } from 'react'; // Wrong: Button re-renders every time parent renders function Button({ onClick, children }) { console.log('Button rendered'); return <button onClick={onClick}>{children}</button>; } function Counter() { const [count, setCount] = useState(0); // New function created every render const increment = () => setCount(c => c + 1); return ( <div> <p>Count: {count}</p> <Button onClick={increment}>Increment</Button> </div> ); } // Right: Use React.memo and useCallback const MemoButton = React.memo(Button); function CounterOptimized() { const [count, setCount] = useState(0); const increment = React.useCallback(() => setCount(c => c + 1), []); return ( <div> <p>Count: {count}</p> <MemoButton onClick={increment}>Increment</MemoButton> </div> ); }
Quick Reference
- React.memo: Wrap components to skip re-render if props unchanged.
- useMemo: Memoize expensive values.
- useCallback: Memoize functions to keep stable references.
- React.lazy + Suspense: Load components on demand to reduce bundle size.
- Keep components small: Smaller components re-render faster.
- Avoid heavy work in render: Move expensive logic outside render or memoize it.
Key Takeaways
Use React.memo to prevent unnecessary re-renders of components with unchanged props.
Memoize expensive calculations with useMemo and functions with useCallback.
Split code with React.lazy and Suspense to improve initial load time.
Keep components small and avoid heavy computations during rendering.
Avoid creating new function or object references inside render without memoization.