React splits an update into a render phase and a commit phase. What work does React do during the render phase, and what does it deliberately leave out of it?
answer
- one phase computes, one applies
- nothing outside React moves yet
- the result may be thrown away
- refs are null, effects have not run
- identical output commits nothing
basics
~20 sThe render phase calls component functions, builds the new element tree, and works out what changed — all in memory, touching nothing outside React. Applying those changes to the DOM, attaching refs, and running effects all belong to the commit that follows.
solid answer
~50 sIn the render phase React calls the component functions that need to run, gets back element trees, and reconciles them against the previous ones to work out what would have to change. All of that is bookkeeping in memory: no DOM node is created, moved, or mutated, no ref is attached, no effect runs, and nothing outside React is observed or written. The commit phase is what applies the computed changes to the DOM and then runs the ref and effect work. The split matters for two reasons. First, because the render phase has no external consequences, React is free to run it at low priority, throw the result away, and start over — which is why your render logic has to be pure. Second, a "re-render" that produces the same output commits nothing, so re-rendering and updating the DOM are not the same event.
go deeper
Remember the order: React calls your function to work out the UI first, and only afterwards changes the actual page. Nothing you return has touched the browser yet.
Be able to list what belongs to each phase — element creation and reconciliation on one side, DOM mutation, refs and effects on the other — and explain why refs are still null in the body.
Use the split when diagnosing: separate the cost of running component functions from the cost of what actually reached the DOM, and say which one your fix targets before proposing it.
Own the reason the split exists at all — a consequence-free compute stage is what makes discardable, re-orderable rendering possible, and your codebase's discipline about purity is what keeps that guarantee real.
## The two phases An update in React runs in two stages. The **render phase** computes what the UI should be. The **commit phase** makes it so. Everything an interviewer wants here follows from the fact that only the second stage has externally visible effects. ## What happens in the render phase 1. React starts at the component with pending work and calls its function. 2. The function returns React elements — plain objects describing type, props and children. Nothing in that object is a DOM node. 3. React reconciles the new elements against what it rendered last time, deciding for each position whether the existing instance can be updated in place, whether it must be replaced, and which props differ. 4. It records the work to be done as flags on its internal tree and continues into the children. 5. Hooks run as part of calling the function, so `useState` reads its current value, `useMemo` may recompute, and `useReducer` applies queued updates. But `useEffect` and `useLayoutEffect` only *register* a callback here; neither callback body runs. At the end of the render phase React holds a complete description of the next UI plus a list of changes, and the real page has not moved at all. ## What is explicitly not in the render phase - **DOM mutation.** No `appendChild`, no attribute writes, no node removal. - **DOM reads.** A layout measurement taken during render describes the *previous* screen, because the current update has not been applied yet. - **Ref attachment.** During render, a ref that will point at a newly created node is still `null`. - **Effects.** Both `useEffect` and `useLayoutEffect` callbacks are commit-phase work. - **Anything with a side effect at all** — network requests, subscriptions, logging, writing to a module-level cache, mutating props or existing state. ## Why the split exists Because the render phase changes nothing outside React, React can treat it as *speculative*. It can start rendering an update, pause partway to handle a more urgent one, discard the half-finished work and redo it from scratch. Discarding is only safe if re-running your component function has no consequence beyond producing a value — which is exactly the purity requirement. In development React makes this concrete: under `<StrictMode>` it calls your component function twice for the same render, and any impure logic shows up as duplicated or contradictory results. The commit phase, by contrast, is not interruptible. Once React begins mutating the DOM it must finish, because a half-updated document is a visibly broken page. ## Re-render is not the same as DOM update This is the practical payoff of the question. Suppose a component re-renders and returns output identical to last time: ```jsx function Badge({ count }) { return <span className="badge">{count}</span>; } ``` If `count` is still `3`, the render phase runs the function, builds a new element object, compares it, finds no difference, and commits nothing for that node. The browser does no layout and no paint for it. So when someone says "this component re-renders 60 times a second, that's why the page is janky", the honest follow-up is: how much of that reached the DOM, and how expensive were the function bodies? Those are two separate costs. ## Why a slow render still freezes the page A common counter-question: if the render phase touches nothing, why can it hurt? Because your component functions execute on the main thread like any other JavaScript. A render that does heavy synchronous work — sorting ten thousand rows, building a huge string — blocks the thread while it runs. The render phase is side-effect-free, not free. ## What this tells you about where code belongs The phase split answers a lot of "where do I put this" questions on its own: - Compute a derived value from props and state → in the component body, during render. - Measure a DOM node, or read a size that depends on the update you just made → after commit, in a layout effect, because during render the node either does not exist yet or still shows the old value. - Send a request, start a subscription, write to something outside React → never during render; it belongs to an effect or an event handler. If you can state the phase a piece of work belongs to, you rarely need to be told the rule separately.
- Can the render phase run more than once for a single committed update?Yes. React may start a render, abandon it because something more urgent arrived, and redo it; and in development StrictMode deliberately calls components twice. Only one render's output is ever committed, but several may have executed. That is precisely why render logic has to be safe to repeat and safe to discard.
- If the render phase never touches the DOM, why can a slow render still freeze the page?Because your component functions are ordinary JavaScript running on the main thread. Sorting a large list or building an expensive string inside a component body blocks the thread for as long as it takes. Being free of side effects is not the same as being free of cost.
- Does a re-render always mean the DOM changed?No. React compares the new output against the previous one and commits only the differences. A component can re-render repeatedly and produce byte-identical output, in which case nothing is written to the document and the browser does no layout or paint for it.
saying these in an interview costs you the question
- Says rendering writes the changes into the DOM
- Reads element sizes during render and trusts the numbers
- Expects a ref to be attached while the component body runs
- Assumes effect callbacks run at the end of render
- Equates number of re-renders with DOM work