Command Palette

Search for a command to run...

Hectal
PHASE 4Beginner ~21 min· topic 8 of 8

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.

main.jsxwhole filejsx
const root = createRoot(document.getElementById("root"));
root.render(<App />); // trigger: initial render

02Trigger: 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).

Counter.jsxwhole filejsx
<button onClick={() => setCount(count + 1)}>Add</button> // trigger: re-render

03Render: 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.

Render: React calls your componentsdiagram
Rendering diagram…

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.

What React does in the commitwhole filejavascript
// 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).

Paint: the browser drawsdiagram
Rendering diagram…

Try it yourself

  1. 1

    Show the hidden state

    In the first example, add <small>ticks: {ticks}</small> to the JSX. Predict how many DOM changes the tick click now makes, then run.

  2. 2

    Change an attribute

    Give the paragraph className={count > 0 ? "big" : ""}. Predict the add report: you should now see an attributes change as well as the text change.

  3. 3

    Swap the element type

    In the Clock example, render the time in an <h2> for the second root.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

Watch exactly which DOM nodes React changes New tab

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

Press Run to see the page.

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"' ]
Re-rendering keeps what the user typed New tab

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

Press Run to see the page.

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? true
Trigger → render → commit → paintdiagram
Rendering diagram…

Break 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>.

terminal
$ Preview
── what you'll see ──
It seems to work, but the render now has a side effect: it runs twice in development, it would run for renders that are thrown away in concurrent mode, and it isn't part of the commit.

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.

terminal
$ Preview
── what you'll see ──
Nothing changes on screen until something unrelated triggers a render, then the new theme appears.

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 useLayoutEffect and ref attachment). Rendering can be interrupted in concurrent mode; the commit never is.

  • ▸

    Setting state to the same value (Object.is equal) 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. 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. 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. 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. 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. 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. 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

01

Name the steps React goes through to update the screen and what happens in each.

02

What are the two things that trigger a render?

03

What does React do to the DOM on a re-render where only one number changed?

04

Why does text in an <input> survive when its parent re-renders?

Practice

01

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.

02

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.