skip to content

In React 19, the render phase can stop partway through the component tree to let the browser handle input or paint. Does that mean a user can end up seeing a partially updated screen, and what happens to the work React had already done before it stopped?

level: juniorimportance: should knowfreq 45%

answer

  1. in-memory tree versus the real document
  2. two phases, only one can stop
  3. the DOM is untouched until the end
  4. resumed from where it stopped, or restarted
  5. abandoned renders make impure components dangerous

basics

~20 s

No. React builds the new tree in memory during the render phase and touches the DOM only in the commit phase, which runs as one uninterruptible block. Paused work is either resumed or discarded and redone.

solid answer

~40 s

The user never sees a half-updated screen. React's render phase only calls component functions and builds an in-memory tree of work; the real DOM still holds the last fully committed UI the whole time React is paused. Nothing becomes visible until the commit phase, and React does not yield inside the commit, so the browser cannot paint between two DOM mutations. As for the paused work itself, one of two things happens: React resumes from the unit it stopped on and finishes the pass, or — if a more urgent update such as a click arrives — it throws the in-progress tree away and starts that render again later. Discarding is safe precisely because render-phase work is pure and lives only in memory.

go deeper

for a junior

Be able to say plainly that the render phase only computes and the commit phase writes to the DOM, so a paused render is invisible to the user.

for a middle

Explain the two outcomes for paused work — resumed from the stopping point, or discarded and restarted after urgent work — and why discarding is safe for pure render work.

for a senior

Connect this to purity in practice: point at real side effects in component bodies that misbehave when a render runs twice or is thrown away, and say where they belong instead.

for a principal

Frame the design tradeoff: React buys responsiveness by making render work disposable, and the price is a purity contract the whole codebase must honour, which is what the compiler and lint rules exist to enforce.

## The two phases React updates the screen in two distinct phases. The **render phase** works out what the UI should look like: React calls your component functions, compares what they return with the previous tree, and builds an in-memory description of the result. Nothing in the document changes during this phase. The **commit phase** takes that finished description and applies it — creating, inserting, removing and updating real DOM nodes, attaching refs, running effects. Only the render phase is interruptible. That single fact answers almost every question about what a user can actually observe. ## What "pausing" means here Concurrent React (the model since React 18, and the default in React 19) walks its internal work tree one node at a time. Between two units of work it asks the scheduler whether it has used up its slice of the current frame — in practice a few milliseconds. If it has, React returns out of its loop, lets the browser run event handlers, layout and paint, and schedules itself to be called back to continue. Note what kind of pause this is. JavaScript runs each task to completion, so React cannot freeze halfway through a function call; it has to genuinely return control and remember where it was in its own data structures. "Interruptible" means "React chooses to stop between units and can find its way back", not "the browser preempts React". ## Nothing half-built is ever shown While React is paused, the document still contains the last committed tree, unchanged. The half-finished new tree exists only as JavaScript objects on the heap. So the user sees the previous UI — not a blend of old and new, not a blank region, and not a Suspense fallback (a fallback appears only when a boundary actually suspends and React commits that fallback). The invariant worth memorising: what is on the screen is always the output of one complete render, never a mixture of two. ## Resumed, or thrown away Two things can happen to paused work. If nothing has changed, React picks up from the unit it stopped on and finishes the same pass. If a higher-priority update arrives in the meantime — a keystroke, a click, a synchronous flush — React abandons the in-progress tree, handles the urgent work first, and renders the lower-priority update again from the root. Restarting is affordable exactly because render-phase work is disposable: it is pure computation over JavaScript objects. Abandoning a half-mutated DOM would not be, which is why React never puts itself in that position. This is also why the Rules of React are a correctness precondition rather than style advice. A render may run more than once for a single update, and it may run for work that is never committed, so a component that mutates something outside itself during render can apply that side effect twice, or apply it for a screen the user never sees: ```jsx function ProductList({ query }) { // Wrong: runs during render, may run twice or for discarded work. analytics.track('search', query); return <ul>{/* ... */}</ul>; } ``` The fix is to move that call into an event handler or an effect, both of which run only for work React actually committed. ## Once committing, React does not stop The commit is a single synchronous block. React does not yield inside it, so the browser has no opportunity to paint between two DOM mutations belonging to the same update. That is what turns "the user never sees a partial tree" from a probability into a guarantee — and it is also why keeping commit-time work small matters, since that block genuinely blocks the frame. ## Practical takeaway You do not have to write anything special to get this behaviour; it is how the renderer works. What you control is which updates React is allowed to interrupt — that is what marking an update as a transition expresses — and whether your components stay pure enough for a discarded render to be harmless.

  • If React can throw away a render it already started, what does that imply about code you put directly in a component body?
    It must be pure. A component body can run more than once per update and can run for a render that is never committed, so anything with an observable side effect — logging an analytics event, writing to a module-level variable, mutating a prop — may fire twice or fire for a screen nobody ever saw. Side effects belong in event handlers or effects, which run only for committed work.
  • Does React ever paint the screen between the render phase and the commit phase of the same update?
    No. The render phase produces an in-memory tree and paints nothing; the commit then applies it in one synchronous block. A paint can happen before React starts committing, and after the commit finishes, but not between the DOM mutations of a single commit. That is what keeps the visible UI consistent with exactly one state snapshot.

saying these in an interview costs you the question

  • Thinks the user briefly sees a half-rendered tree
  • Believes React writes to the DOM as it renders each component
  • Says a paused render shows the Suspense fallback
  • Assumes interrupted render work is always resumed, never discarded
  • Thinks the browser preempts React mid-function-call

context