Skip to content

Repository files navigation

🧬 Why children don't re-render — the composition boundary in React

An interactive React demo of a fact that feels like magic until you know it: a component passed as children (or any element prop) does not re-render when its parent re-renders — because the parent didn't create it.

▶ Live: https://children-rerender.pages.dev/

Bump each wrapper's state and watch the <Child> render counter. Same child, two ways of placing it — completely different behaviour.

The two cases

Wrapper creates the child — the child re-renders every time the wrapper does:

function Wrapper() {
  const [n, setN] = useState(0);
  return (
    <div onClick={() => setN(n + 1)}>
      <Child />        // created HERE → new element on every Wrapper render
    </div>
  );
}

Child passed as children — the child stays put:

function Wrapper({ children }) {
  const [n, setN] = useState(0);
  return (
    <div onClick={() => setN(n + 1)}>
      {children}       // created by the PARENT → same element across re-renders
    </div>
  );
}

<Wrapper><Child /></Wrapper>   // App makes <Child/>, once

In the demo: bump the first wrapper and both counters climb together; bump the second and only the wrapper counter moves — the child holds at 1.

Why

A component re-renders when either:

  1. its own state or props change, or
  2. its parent re-renders and hands it a freshly-created element.

children is created by the grandparent. When the wrapper re-renders, it hands back the same element reference it received — so React sees nothing changed for that subtree and bails out of re-rendering it. No memo involved; it's structural.

Why it's useful

It's a free re-render boundary. If a component holds fast-changing state (a hover position, an input value, an animation frame) but wraps expensive content, take that content as children instead of rendering it inline. The state updates re-render the wrapper; the heavy children don't. This is exactly how <Context.Provider value={...}>{children}</Context.Provider> avoids re-rendering the whole tree, and it's the reasoning behind "lift expensive JSX up and pass it down."

Caveat: this only helps when the children genuinely don't depend on the wrapper's changing state. If they do, they should re-render — that's correctness, not waste.

Stack

React 19 · TypeScript · Vite. StrictMode is off so the render counts are real, not the dev-mode double-invoke.

Run locally

npm install
npm run dev

License

MIT © 2026 dev48vdev48v.infy.uk

About

Interactive React demo — a component passed as children (or any element prop) doesn't re-render when its parent re-renders, because the parent didn't create it. Live render counters prove it; a free alternative to React.memo and how Context.Provider avoids re-rendering the tree. React 19 + TS + Vite.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages