Stop guessing about React re-renders

April 8, 20265 min readBy Harman Kamboj
Full-stackEngineering

Most performance bugs I get asked to look at in React come from a fuzzy mental model of re-renders. Someone read that re-renders are bad, wrapped half their components in memo, and now the code is harder to read and no faster. React re-renders are not inherently expensive, and most of them do not matter at all. The skill is knowing which ones do, and that starts with understanding what actually triggers one in the first place.

What actually causes a re-render

A component re-renders for a small number of reasons. Its own state changed, its parent re-rendered, or a context it consumes changed. That is essentially the list. The one people misunderstand most is the second one. When a component re-renders, all of its children re-render by default, regardless of whether their props changed. This is by design and it is usually fine, because rendering is just React calling your function and diffing the result, not touching the DOM.

The word render is overloaded and that is half the confusion. A render in React means running the component function and producing a description of the UI. That is cheap. The expensive part is committing changes to the actual DOM, and React already skips that when nothing changed. So a component re-rendering does not mean the browser repainted. Hold that distinction and a lot of the panic goes away.

When re-renders actually hurt

Re-renders become a real problem in a few specific situations. You have a genuinely large tree, like a long list or a complex grid, and a parent re-render cascades through thousands of nodes. You have a component doing expensive work in its render body, such as a heavy computation or sorting a big array, that re-runs needlessly. Or you have a high-frequency update, like something tied to scroll or a fast input, fanning out across a wide subtree on every tick.

If you do not have one of those, you almost certainly do not have a re-render problem, and adding memoization is pure cost with no benefit. I have deleted plenty of memo wrappers that were guarding components that render in well under a millisecond. They added mental overhead and protected nothing.

  • Large lists and grids where a parent update cascades widely
  • Expensive computation living directly in a render body
  • High-frequency updates from scroll, drag, or fast typing

The fixes, in the order I reach for them

My first move is almost never memo. It is to move state down. If only a small part of the tree cares about a piece of state, push that state into a smaller component that owns it, so changing it does not re-render everything above and beside it. A huge share of cascade problems are really a state-placement problem wearing a costume. Lift state only as high as it genuinely needs to live, and no higher.

The second move is to stop creating new objects and functions in render when they feed into something that compares by reference. If you pass a fresh object or callback to a memoized child or into a dependency array on every render, you have defeated the optimization before it started. useMemo and useCallback are tools for keeping references stable, not magic speed buttons. And React.memo on a child only helps if its props are actually stable across renders, which is why people who add it without fixing the props see no change.

Measure before you touch anything

The React DevTools profiler will tell you exactly which components rendered, how often, and how long each took. Turn on the highlight-updates option and you can literally see what flashes when you interact. This takes two minutes and it replaces all the guessing. I have lost count of how many times the component someone was sure was the problem turned out to render in a fraction of a millisecond while the real cost was somewhere they never looked.

Optimizing without measuring is how you end up with a codebase full of defensive memoization that nobody can safely remove because nobody knows what it was protecting. Profile first, find the component that is actually slow or rendering far too often, fix that one thing, and profile again to confirm it worked.

The mindset that fixes this for good

Treat re-renders as normal and cheap until a profiler tells you otherwise. React was built around the idea that re-rendering is the default and the framework handles making it efficient. Your job is to give it stable references where they matter, keep state close to where it is used, and intervene only at the spots that measurement flags. Everything else is the framework doing what it is supposed to do.

When you stop guessing and start profiling, React performance stops feeling like a dark art. The problems are almost always concrete and local: one big list, one expensive calculation, one piece of state living too high in the tree. Find that, fix that, and leave the rest of your components alone. They were never the problem.

Building something where this matters?

I am open to senior full-stack, Web3, or AI engineering roles, fully remote and any timezone. If the hard part of your product is fighting you, that is the work I like.

Get in touch →