Skip to main content

Render and Commit

Before anything shows up on screen, React puts your components through three steps. Getting these three clear in my head is what makes later topics (state as a snapshot, batching, effects) actually make sense.

The three steps are: trigger the render (something asks React to run), render the components (React calls your functions to work out the UI), and commit to the DOM (React applies the result to the screen).

Step 1: Trigger a render

There are only two reasons a component renders. Either it's the initial render (the app is starting up), or its state changed (its own, or an ancestor's).

Initial render

When the app first starts, React does the initial render. This is the one time you call createRoot with the target DOM node and then call its render method.

index.jsx
import { createRoot } from "react-dom/client";

const root = createRoot(document.getElementById("root"));
root.render(<Image />);

See createRoot for the full API.

Re-renders when state updates

After the first render, every later render is triggered by a state change. Calling a set function updates the state and automatically queues a new render. So each state update is what sends a fresh request for React to render again.

Step 2: React renders your components

"Rendering" simply means React calls your component function to work out what should be on screen. It's not React touching the DOM yet, it's React asking your components "what do you want to show?"

This step is recursive. On the initial render React calls the root component. On a re-render it calls the function whose state changed. Either way, if that component returns another component, React calls that one next, and keeps going down the tree until there's nothing nested left to call. By the end, React knows exactly what the whole UI should look like.

So if a Gallery returns three Image components, rendering means React calls Gallery(), and then Image() for each one, building up the full picture.

The difference between the first render and later ones is what React does with the result. On the initial render React creates the DOM nodes. On a re-render React compares this render's output against the previous one and figures out what actually changed. It doesn't change the DOM in this step, though. That's step 3.

Pitfall: rendering must be a pure calculation. Two rules. Same inputs, same output: given the same props, state, and context, a component must always return the same JSX. And it minds its own business: it must not change any objects or variables that existed before it rendered. Break either one and you get confusing, hard-to-trace bugs as the app grows. This is why Strict Mode calls each component twice in development, so impure code shows itself.

Step 3: React commits changes to the DOM

Now React actually updates the DOM. How it does this depends on whether it's the first render.

On the initial render, React uses the appendChild() DOM API to put every node it created onto the screen.

On a re-render, React applies only the minimal operations needed to make the DOM match the latest output. The important part: React only touches a DOM node if it actually differs from the last render. If the render produced the same result as before, React leaves the DOM completely alone.

This is easier to see with an example. Imagine a component that re-renders every second to show a ticking clock, and it also has an <input> you can type into.

Clock.jsx
export default function Clock({ time }) {
return (
<>
<h1>{time}</h1>
<input />
</>
);
}

Even though the whole component re-renders every second, whatever you've typed in the <input> does not disappear. React sees the <input> is in the same spot in the JSX as last time, works out that only the <h1> text changed, and updates only the <h1>. The <input> and its current value are left untouched. That selective updating is the whole payoff of step 3.

Deep dive: optimizing performance. By default, when a component re-renders, React re-renders everything nested inside it too. If that component sits high in the tree, that can be a lot of work. There are opt-in ways to fix this if you hit a real performance problem (see the Performance docs), but the advice is firm: don't optimize prematurely.

Epilogue: browser paint

Once React finishes committing to the DOM, the browser redraws the screen. The docs call this step painting, to keep it separate from React's own "rendering" step. It's a useful distinction: React's render is calculating the UI in JS, the browser's paint is the pixels actually changing.

Recap

Every screen update in a React app happens in the same three steps: trigger, then render, then commit, followed by the browser painting. A render is triggered by the initial startup or by a state change. Rendering means React calls your components (which must be pure) to figure out what to show. Committing means React updates the DOM, and it only touches the nodes that actually changed, leaving everything else as it was. Strict Mode helps catch impure components, and because React skips unchanged nodes, an update that produces the same result as before touches the DOM not at all.