Chapter 22: Render and Commit
Core Idea
Every screen update goes through three distinct steps — trigger, render, commit — and understanding them explains why rendering (React calling your components) doesn't automatically mean the DOM changes.
Key Concepts
- Step 1 — Trigger: a render happens for exactly two reasons — the component's initial render (
createRoot(node).render(<App />)), or a state update (calling a useState setter) on that component or an ancestor.
- Step 2 — Render: "rendering" literally means React calling your component functions to figure out what should be on screen. On initial render, React calls the root component; on a re-render, it calls the component whose state changed. This is recursive — if a called component returns another component, React calls that one next, and so on, until there's nothing left to resolve.
- Rendering must be pure (see Ch 18): same inputs → same JSX, no mutating anything that predates the call. Strict Mode's double-invoke-in-dev behavior exists specifically to surface violations of this.
- Step 3 — Commit: only after all rendering resolves does React touch the DOM. On initial render it uses
appendChild()-style APIs to place everything; on a re-render, it applies only the minimal diff calculated during the render step — React only changes DOM nodes where the render output actually differs from last time.
- Concrete proof of the diffing: a
<Clock> component re-rendering every second (new time prop) alongside an <input> the user is typing into — the <input>'s typed text survives every re-render because React sees the <input> element is in the same JSX position and leaves its DOM node (and thus its live value) untouched, updating only the <h1> text.
- Browser paint is a separate, later step: after React commits to the DOM, the browser repaints the screen — that's a browser-level step outside React's control, referred to as "painting" in the docs to avoid confusing it with React's own "rendering."
Key Takeaways
- "Render" and "commit" are not the same thing — a component can be called (rendered) without any DOM node actually changing, if its output is identical to last time.
- The
<input>-keeps-its-text-during-unrelated-re-renders behavior isn't magic — it's a direct consequence of React only touching DOM nodes whose calculated output differs, combined with element-position matching.
- Rendering all the way down from an updated component is the default and usually fine; treat it as a performance concern only after profiling shows it's actually slow (don't optimize prematurely).
Connects To
- Ch 18 (Keeping Components Pure): the purity requirement this chapter's "render" step depends on.
- Ch 21 (State: A Component's Memory): what actually triggers a re-render in the first place.
- Ch 30 (Preserving and Resetting State): the deeper rules behind "same position in JSX = same DOM node preserved."