React rendering is one of the most misunderstood concepts in modern frontend development. Most performance advice you read online treats it as something to fight against. It isn't. Rendering is cheap; the expensive part is what you do during it.
What rendering actually means
Rendering is the process where React calls your component function to find out what the UI should look like. It does not mean touching the DOM.
There are three distinct phases:
- Render — React calls your component and builds a description of the UI.
- Reconcile — React diffs that description against the previous one.
- Commit — React applies the minimal set of DOM mutations.
Only the commit phase talks to the browser. A component that renders a hundred times but commits nothing is usually fine.
What triggers a render
A component re-renders when one of these happens:
- Its own state changes.
- Its parent re-renders.
- A context it consumes changes value.
That second point is the one that surprises people. Props being "the same" does not stop a re-render — React has no way to know your component is pure unless you tell it.
function Parent() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount((c) => c + 1)}>{count}</button>
{/* Child re-renders on every click, even though it takes no props */}
<Child />
</div>
);
}The fix is usually structural
Before reaching for memo, move the state down to the component that actually uses it:
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount((c) => c + 1)}>{count}</button>;
}
function Parent() {
return (
<div>
<Counter />
<Child />
</div>
);
}Now Child never re-renders, and there is no memoization to keep correct.
Memoization is a cache. Every cache is a correctness risk plus a maintenance cost. Reach for structure first.
When memoization does help
| Situation | Use |
|---|---|
| Genuinely expensive computation | useMemo |
| Stable identity for a dependency array | useCallback |
| Large list where the parent updates often | memo |
| Everything else | Nothing |
Measure with the React DevTools Profiler before and after. If you cannot see the difference in a flame chart, you have added complexity for nothing.
The short version: renders are not the problem. Rendering the wrong subtree is, and the cheapest fix is almost always moving state closer to where it is read.