Why Your React App Re-Renders Too Much (And How to Stop It)
You built a React component. It works. But something feels sluggish — inputs lag, lists flicker, the UI feels heavier than it should be. You open React DevTools Profiler and find that a single state change is.
You built a React component. It works. But something feels sluggish — inputs lag, lists flicker, the UI feels heavier than it should be. You open React DevTools Profiler and find that a single state change is triggering dozens of component re-renders across the tree.
This is one of the most common performance problems in React applications, and it's almost always fixable.
First, Understand Why React Re-Renders
React re-renders a component when its state changes, its props change, or its parent re-renders. That last one is the sneaky one. When a parent re-renders, every child re-renders by default — even if the child's props didn't change at all.
In a large component tree, one state update at the top can cascade into 30+ unnecessary re-renders below it.
The Tools React Gives You
React.memo — Wrap a functional component in React.memo and it will only re-render if its props actually change. This is the first and most important tool. Use it on components that are expensive to render and receive the same props frequently.
useMemo — Use this to memoize the result of an expensive calculation so it doesn't recompute on every render. Only use it when the calculation is genuinely expensive — don't wrap 2 + 2 in useMemo.
useCallback — Functions get recreated on every render. If you're passing a function as a prop to a memoized child component, wrap it in useCallback so the reference stays stable and the child doesn't re-render unnecessarily.
The Most Common Cause: Object and Array Props
This is where most developers get tripped up. Consider this:
jsx<MyComponent config={{ theme: 'dark' }} />
Every time the parent renders, { theme: 'dark' } creates a brand new object with a new reference in memory. To React's comparison, the prop changed, even though the value is identical. React.memo won't help here. The fix is to define the object outside the component or memoize it with useMemo.
State Structure Matters Too
Storing too much in a single state object near the top of your tree is a common mistake. When any field in that object changes, everything subscribed to it re-renders. Split your state into smaller, more focused pieces. Keep state as close to where it's used as possible — this is called "co-locating state" and it's one of the best things you can do for React performance.
When Not to Optimize
Don't premature-optimize. Adding useMemo and useCallback everywhere adds cognitive overhead and can actually hurt performance if the memoization cost exceeds the re-render cost. Profile first. Identify the actual bottleneck. Then fix specifically that.
React DevTools Profiler is free, built-in, and takes 2 minutes to learn. Use it before you start wrapping things in hooks you don't need.
Most React performance problems have simple solutions. The hard part is knowing where to look.