Topic 4.8
Render and Commit: How React Updates the Screen
In one line
Every screen update goes through three steps: something triggers a render (the first mount or a state change), React renders by calling your components to work out what should change, and React commits by touching only the DOM nodes that differ; then the browser paints.
Think of it like this
A restaurant kitchen. A customer orders (trigger). The cook prepares the dish in the kitchen, out of sight (render). The waiter serves it at the table (commit). And the diner sees it on the table (paint). If a customer orders the same dish again, the kitchen may cook, but the waiter doesn't swap the plate if nothing on it is different.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Trigger
- The reason a render happens: the first mount, or a state update.
- Initial render
- The very first render, when the app starts and React creates all the DOM nodes.
- Re-render
- A later render, caused by a state update, that recalculates part of the UI.
- Render (React's meaning)
- React calling your component functions to calculate what the UI should look like.
- Commit
- React applying the changes it found to the real DOM.
- Reconciliation
- Comparing the new elements with the previous ones to find the smallest set of DOM changes.
- Paint
- The browser drawing the updated page on the screen after the DOM has changed.
- Pure render
- A render that only calculates its output and changes nothing outside the component.
Step by step
01Trigger: initial render
The app starts. root.render(<App />) triggers the very first render of the whole tree.
const root = createRoot(document.getElementById("root"));
root.render(<App />); // trigger: initial render02Trigger: a state update
Later, a setter call queues an update and triggers a re-render of that component (after the event handler finishes, Topic 4.5).
<button onClick={() => setCount(count + 1)}>Add</button> // trigger: re-render03Render: React calls your components
React calls the component that changed and then the components it returns, down the tree. Each call returns elements. Nothing on the page changes yet.
04Commit: only the differences reach the DOM
React compares the new elements with the previous ones. Here only one text changed, so React updates exactly one text node. The <h1>, the <button> and the <input> are left alone.
// initial render: build everything
root.appendChild(div); div.appendChild(h1); /* ... */
// re-render where only count changed: one operation
textNode.nodeValue = "1";05Paint: the browser draws
With the DOM updated, the browser lays out the page and paints it. Then effects run (Phase 7).
Try it yourself
- 1
Show the hidden state
In the first example, add
<small>ticks: {ticks}</small>to the JSX. Predict how many DOM changes thetickclick now makes, then run. - 2
Change an attribute
Give the paragraph
className={count > 0 ? "big" : ""}. Predict theaddreport: you should now see anattributeschange as well as the text change. - 3
Swap the element type
In the Clock example, render the time in an
<h2>for the secondroot.render(pass a prop that picks the tag). Is the heading the same node? (No: a different type is torn down and rebuilt, Topic 6.4.)
Code & diagrams
Re-render only: App rendered, but its output was identical, so React committed nothing. Add: React changed one text node, the number. The heading, buttons and paragraph element were not touched.
Preview
Console
Nothing logged yet.Expected console output
render: count = 0 | ticks = 0
render: count = 0 | ticks = 1
tick: DOM changes = 0 []
render: count = 1 | ticks = 1
add: DOM changes = 1 [ 'characterData on #text -> "1"' ]The second `root.render` is a re-render of the same tree. React kept the `<h1>` and the `<input>` nodes, changed the time text only, and the typed note and the focus survived. (Real apps update with state, not repeated `root.render` calls; this just makes the trigger visible.)
Preview
Console
Nothing logged yet.Expected console output
Clock rendered with 10:00:00
Clock rendered with 10:00:01
same <h1>? true | text: 10:00:01
input still says: my note
input still focused? trueBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Change the DOM yourself during render
Inside App, before return, write document.title = "Count " + count; and wrap the app in <StrictMode>.
Break #2
Expect a render without a trigger
Change a module-level variable (let theme = "dark") from a button and expect components that read it to update.
Myth vs fact
Myth
Rendering means updating the DOM.
Fact
In React, rendering is calling your components. Updating the DOM is the commit, and it only happens for what changed. Painting is the browser's step after that.
Myth
A re-render replaces the component's DOM.
Fact
React reuses DOM nodes whenever the element type and position (and key) match, changing only attributes and text that differ.
Myth
If a component re-rendered, the screen changed.
Fact
Many renders produce the same output and commit nothing. Re-rendering is the normal, cheap way React checks what's needed.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Internally, render builds a work-in-progress fiber tree and marks fibers with effects (insert, update, delete); the commit walks those marks in one synchronous pass (mutation phase, then layout effects such as
useLayoutEffectand ref attachment). Rendering can be interrupted in concurrent mode; the commit never is. - ▸
Setting state to the same value (
Object.isequal) normally skips rendering. In some cases React still calls the component once to be sure, then throws the result away without rendering the children or committing: your component must tolerate that. - ▸
Because only the commit is visible, React can prepare renders in the background (
useTransition, Suspense,<Activity>) and discard them. That's why render must be pure: a discarded render must leave no trace. - ▸
Chrome DevTools' Performance panel shows the split: React's render and commit appear as scripting, followed by Layout and Paint. React DevTools' Profiler (Topic 16.6) shows which components rendered in each commit and why.
Remember this
- 1
Step 1: trigger. There are only two reasons React renders a component: the initial render, when the app starts (
createRoot(...).render(<App />)), and a re-render, when its state (or a parent's state, or a context it reads) is updated with a setter. Nothing else (changing a variable, mutating an object) starts a render. - 2
Step 2: render. Rendering means React calls your component functions. On the initial render it calls the root component; on a re-render it calls the component whose state changed, and then, recursively, the components it returns (its children), because their props may have changed. Rendering only calculates: it produces React elements describing the UI. It must be pure: same inputs, same output, and no changes to anything outside (Topic 2.6). Rendering doesn't touch the DOM.
- 3
Step 3: commit. After rendering, React commits the result to the DOM. On the initial render it creates all the DOM nodes with
appendChild. On a re-render it compares the new elements with the previous ones (reconciliation) and applies the minimal set of changes: change a text node, set an attribute, insert or remove a node. React touches only the DOM nodes that changed. If a render produces the same output as before, React commits nothing for it at all. - 4
This is why input text, focus, scroll position and selection survive re-renders: React doesn't rebuild elements it can reuse. A
<input>that stays in the same place with the same type is the same DOM node after the update, so whatever the user typed is still there. - 5
Step 4: the browser paints. After React has updated the DOM, the browser recalculates layout and paints pixels on the screen. React's work is finished before this happens; it's the browser's job. People sometimes call painting "browser rendering"; to avoid confusion, React's docs say "painting" for the browser and "rendering" for React calling your components.
- 6
Rendering is usually cheap, so React re-renders freely; a render that ends with no DOM change costs only the function calls. When renders do get expensive, Phase 16 shows how to measure and skip them (
memo, the React Compiler). Effects (Phase 7) run after the commit and paint, which is where you put code that must talk to the real DOM or the outside world.
Explain it without notes
Name the steps React goes through to update the screen and what happens in each.
What are the two things that trigger a render?
What does React do to the DOM on a re-render where only one number changed?
Why does text in an <input> survive when its parent re-renders?
Practice
A component logs render each time it runs. A button calls setName("Asha") when name is already "Asha", and another calls setName("Ravi"). Describe what happens in render and commit for each button.
List the steps for this event: the user clicks "Like", which calls setLikes(likes + 1) in Post, and Post renders a <LikeButton> and a <Comments> child.
Trade-offs
- ↔
Re-rendering whole subtrees keeps the model simple and is usually cheap; when profiling shows a slow render, skip work with
memo, moving state down or the React Compiler (Phase 16) rather than avoiding state.
Done when you can
I can name and explain trigger, render, commit and paint.
I know the two causes of a render.
I know rendering is pure calculation and never touches the DOM.
I can explain why React changes only the DOM nodes that differ.
I can explain why typed text and focus survive a re-render.