DEV Community

Cover image for React Render vs Commit: What Actually Happens (DOM vs React Native)
john mbugua
john mbugua

Posted on

React Render vs Commit: What Actually Happens (DOM vs React Native)

I know we’ve all heard these phrases: “React rendering” and “React commit” and if you’ve ever profiled a React Native app, you’ve probably seen logs or profiler entries like “committed.” It’s an interesting topic: how React works when it’s rendering and committing changes, whether that’s to the DOM (on the web) or to native UI views (in React Native).

So let’s start with something small but important: React’s render phase and commit phase. We’ll look at how updates get scheduled across the component tree, what actually causes React to render, what reconciliation means, why some renders don’t result in visible UI changes, and how optimization starts to matter once rendering becomes a performance bottleneck.

What causes a component to render?

A component renders when React thinks something in that part of the tree might have changed. The most common triggers are:

  • State updates (useState, useReducer, class setState)
  • Props changes from a parent
  • Context updates (any consumers of that context may re-render)
  • Store updates (Redux/Zustand/MobX subscriptions)
  • A parent re-render, which can cause React to re-run children while computing the next UI

Once any of these happen, React schedules work and begins the update process. And this is where the two phases come in.

The two-phase model: render phase & commit phase

The easiest way to think about React updates is:

Render phase computes the next UI. Commit phase applies it to the real platform UI.

Render phase

On the left side of the image is the Render Phase. This is where React does planning, not painting.

1) JSX becomes React elements

In my diagram, JSX flows into a React element via createElement(). That’s exactly the idea:

  • JSX is just syntax.
  • It becomes React elements (plain objects describing type + props + children).
  • This step is the same in React DOM and React Native.

React elements are not DOM nodes and not native views, they’re the description of what you want.

2) Current tree vs Work-in-progress tree

When an update happens (state/props change), React doesn’t immediately touch the screen. Instead, it builds a new version of the UI internally:

  • Current tree: the last committed version (what the user is currently seeing)
  • Work-in-progress tree: the next version React is building based on the new state/props

3) Reconciliation

That “Compare React Element/Reconciliation” arrow in my image represents the core job of the render phase.
React compares what the UI looked like before (current) with what it should look like now (work-in-progress) and figures out:

  • what stays the same
  • what needs to update
  • what should be added
  • what should be removed

4) Output of render phase

At the end of the render phase, React produces what I have called in the diagram changes, basically a list of “things the renderer must do.”

The screen hasn’t changed yet. React has only computed what should change.

Commit phase

Now we move to the right side of the image the Commit Phase. This is where React stops planning and starts applying. React core hands the “changes” to a renderer.

React Native commit

React Native applies those changes by creating/updating/removing native view instances, such as:

  • View
  • Text
  • Image

So commit in React Native means: update the native UI tree.

React DOM commit

React DOM applies those changes by creating/updating/removing DOM nodes,elements in the browser DOM tree.
So commit on the web means: update the DOM.

Unnecessary renders

Now that we’ve seen the big picture (render phase - commit phase), this second diagram shows a very common performance situation: a component re-renders, but nothing visually changes. That’s what people usually mean when they say unnecessary (or wasted) renders.

The setup

In the diagram, the Parent component is flagged because its state or props changed. That could be a button press, a text input update, a timer tick, a fetch response, anything that triggers a state/props update.

Once the parent is flagged, React schedules work and starts the render phase.

What happens in the render phase (and why children get involved)

During the render phase, React re-runs the parent to compute the next UI. But it doesn’t stop there.

Because the parent’s output may include children, React will typically continue into the subtree and re-run child components too, so it can build the next work-in-progress tree.

That’s the key idea of the diagram is showing:

  • Parent changes → React re-runs Parent
  • React attempts to render its children as part of building the next tree
  • React produces the next output (React elements)
  • React compares **Current tree vs **Work-in-progress tree (reconciliation)

The “unnecessary render” moment

Here’s the important part: the child re-rendered, but its output didn’t change.

So what does React do?

  • The child component function still ran (render work happened)
  • Reconciliation still compared old vs new output
  • But since the child output was the same, React produces no host updates for that child

That means when React gets to the commit phase, there’s nothing to apply for that child subtree:

  • React Native: no new/updated/removed native view instances for that child
  • React DOM: no meaningful DOM mutations for that child

So the render happened, but the commit work for that child was basically a no-op.

This is why it’s called wasted work:

You paid the cost of re-running the component and reconciling it, but you didn’t get any UI change.

Why unnecessary renders matter in profiling

When profiling, this is exactly why you might see:

  • many component renders,
  • but very little commit activity,
  • and still feel the app getting slower (especially on lower-end devices).

Because the cost you’re paying is mostly on the JavaScript side

  • executing component functions
  • running hooks
  • reconciling children

Even if commit is small, lots of wasted render work can still block the JS thread and create jank.

Unnecessary renders happen when a parent update causes a child to re-render during the render phase, but the child’s output stays the same, so there are no actual UI changes to commit.

Top comments (0)