AniUI Academy

Why Components Re-render

The exact triggers that cause a component to render again, why a parent re-rendering renders its children by default regardless of props, and why render isn't the same as a DOM change.

8 min read

The next few lessons are about performance — but performance work on anything is only useful once you know precisely what's actually happening, so this lesson is entirely about the mechanics of when and why a component re-renders at all, before touching any of the tools for changing that.

The four triggers

A component's function runs again — re-renders — for exactly one of these reasons:

  1. Its own state changed — a useState or useReducer setter was called with a new value.
  2. Its own props changed — its parent passed different values this time.
  3. Its parent re-rendered — covered in detail below; this one surprises people.
  4. A context it consumes changed — from the context lesson, any Provider value change re-renders every consumer.

Reading a ref, by contrast, never causes anything to re-render — that's the entire distinction useRef exists to provide.

The one that surprises people: parent re-renders, child follows

React's default is unconditional: if a parent component re-renders, every child it renders re-renders too — regardless of whether that specific child's props actually changed.

function App() {
  const [count, setCount] = useState(0);
 
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Clicked {count} times</button>
      <ExpensiveChild /> {/* re-renders every time count changes, even with no props at all */}
    </div>
  );
}

ExpensiveChild takes no props, and nothing about it depends on count — but it re-renders every single time App does, simply because it's part of what App returns. This is intentional simplicity: React's default algorithm doesn't try to guess whether skipping a child is safe, it just always re-renders the whole subtree from wherever the change originated downward. The next lesson, React.memo, is the explicit opt-in for telling React "actually, skip this one if its props didn't change."

Re-rendering isn't the same as a DOM change

It's worth separating two things that get conflated: a component's function running again (render) versus the browser's DOM actually being mutated (commit). From the reconciliation lesson, these are distinct phases — render produces a new element tree, and only if that tree differs from the last one does anything get committed to the real DOM.

function Clock() {
  const [tick, setTick] = useState(0);
 
  useEffect(() => {
    const id = setInterval(() => setTick((t) => t + 1), 1000);
    return () => clearInterval(id);
  }, []);
 
  return <p>Static label</p>; // never actually changes, regardless of tick
}

This component re-renders every second (state changes every second), but its output — <p>Static label</p> — is identical every time. React still calls the function again each time, but the diff finds no actual difference, so nothing is committed and the browser does zero extra work on the real DOM.

Why "it re-renders a lot" isn't automatically a problem

A component function running again is, by itself, ordinary JavaScript execution — for a typical small component, that's fast enough to be irrelevant. The place performance work actually pays off is where a render does something expensive: a large computation over a big array, or a render that's re-triggering equally expensive children. Reaching for React.memo or useMemo on a Clock-sized component because it "re-renders a lot" optimizes something that was never slow — the tools in the next two lessons matter for renders you've actually confirmed cost something, not as a reflex against the word "re-render" itself.

Try it yourself

Watch how a component with no relevant props still re-renders whenever its parent does — the console-style log below increments on every click, even for the child that receives nothing:

Try it yourself
Loading playground...

What to remember

  • A component re-renders when its own state changes, its own props change, its parent re-renders, or a context it consumes changes.
  • By default, a parent re-rendering re-renders every child in its tree, regardless of whether that child's props actually changed — this is what React.memo, next, opts out of.
  • Render and commit are separate: a component can re-render and produce an identical output, in which case nothing is actually mutated in the real DOM.
  • "Re-renders often" isn't automatically a performance problem — the cost that matters is an expensive render, not the act of rendering itself.

Check yourself

4 questions · pass 3/4 to unlock React.memo

up to 50
  1. 1.Which of these, by itself, causes a component to re-render?

  2. 2.A parent re-renders, and passes the exact same prop values to a child as last time. Does the child re-render too, by default?

  3. 3.If a component 're-renders', does that necessarily mean the real DOM changed?

  4. 4.Why is 'this component re-renders too often' not automatically a performance problem worth fixing?

4 left to answer