Free lesson · React Deep Dive
What "Render" Means (The Precise Definition)
Lesson 13.1 - What "Render" Means (The Precise Definition)
The vocabulary lesson.
The word "render" is overloaded in React discourse. It's used to mean different things in different contexts, often within the same sentence. Most engineers carry a fuzzy mental model where "render" sometimes means one thing and sometimes another - and that fuzziness causes specific kinds of bugs.
This lesson disambiguates. By the end, you'll have precise vocabulary for the four distinct phases - render, reconcile, commit, paint - and you'll know what each one actually is, when it happens, and what work it does.
The framing for the lesson is one observation: the word "render" in React refers to one specific phase, not the whole process of updating the screen. The other phases have their own names. Conflating them produces confused debugging. Once you have the vocabulary, the timing questions become much clearer.
The Four Phases
A complete update of what appears on the screen has four phases, in this order:
flowchart LR
Trigger["Trigger<br/>(state change,<br/>parent re-render,<br/>context change)"]
Render["Render phase<br/>(call your component function;<br/>produce JSX/elements)"]
Reconcile["Reconciliation<br/>(diff against previous;<br/>compute DOM changes)"]
Commit["Commit phase<br/>(apply DOM mutations)"]
Paint["Paint<br/>(browser draws pixels)"]
Trigger --> Render --> Reconcile --> Commit --> Paint
style Render fill:#a78bfa,stroke:#8b5cf6,color:#000
style Commit fill:#34d399,stroke:#10b981,color:#000
style Paint fill:#fbbf24,stroke:#f59e0b,color:#000
A timeline of the four phases that produce a screen update. A trigger - like a state change, a parent re-render, or a context value changing - initiates the cycle. The render phase: React calls your component function and the function returns JSX, which is actually React.createElement calls under the hood. Reconciliation: React compares the new element tree to the previous one and computes the minimum set of DOM operations needed. The commit phase: React applies those DOM mutations synchronously. Paint: the browser draws the updated pixels to the screen. Each phase is distinct work. In React's terminology, "render" refers specifically to the second phase - the function call. The DOM update happens in the commit phase. The pixels appearing happens during paint. Confusing these phases is the most common cause of confused debugging in React.
Walk through this carefully:
1. Trigger. Something happens that tells React work needs to be done. State setter called, parent re-rendered, context value changed.
2. Render. React calls your component function. The function returns JSX - which is actually React.createElement calls under the hood, producing a tree of plain JavaScript objects describing what the UI should look like.
3. Reconciliation. React compares the new tree to the previous tree. It figures out the minimum set of DOM operations needed to make the DOM match the new tree. (M14 covers this in depth.)
4. Commit. React applies the DOM mutations from reconciliation. Adds nodes, removes nodes, updates props.
5. Paint. The browser, on its own schedule, draws the updated DOM to the screen pixels.
The crucial point: "render" in React vocabulary refers specifically to phase 2. It's the function call. It's not the DOM update; it's not the pixels appearing. Those are separate phases with separate names.
What "Render" Specifically Means
To be unambiguous: a render is React calling your component function and the function returning JSX.
That's the entire definition. Nothing about the DOM. Nothing about pixels. Just: React invokes the function; the function runs; the function returns JSX.
function Counter() {
const [count, setCount] = useState(0)
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(c => c + 1)}>+</button>
</div>
)
}
When React "renders" Counter, what literally happens:
- React calls
Counter()(no arguments - props come from JSX usage sites, captured during reconciliation). useStateruns, returning the current count and a setter (read from React's stored state per M11.2).- The function executes the JSX, which evaluates to a tree of
React.createElementcalls. - The function returns that tree.
After the function returns, React has a tree of elements. It compares this tree to the previous render's tree. It figures out what DOM changes to make. Then it commits them. None of that is "the render" - the render was just the function call.
What JSX Actually Returns
A useful intermediate detail. JSX is syntactic sugar. The function:
function Counter() {
return (
<div>
<p>Hello</p>
</div>
)
}
Compiles (via Babel or similar) to:
function Counter() {
return React.createElement('div', null,
React.createElement('p', null, 'Hello')
)
}
React.createElement returns a plain JavaScript object:
{
type: 'div',
props: { children: { type: 'p', props: { children: 'Hello' } } },
// ... other fields
}
A render is the function call that produces this object. The object is the result of rendering. The object is not the DOM. It's a description of what the DOM should be.
This distinction matters: rendering is fast because it's just function calls and object creation. The actual DOM work happens later in the commit phase, which is separate and potentially batched.
Common Misconceptions
Let me walk through specific misconceptions and clarify each.
Misconception 1: "Render Updates the DOM"
The most common confusion. The render phase doesn't touch the DOM. It produces a description (the element tree). The commit phase touches the DOM.
This matters because:
- Render is usually fast. Calling a function and creating objects is cheap.
- Commit can be slow if there are many DOM mutations. But even a slow commit isn't "the render being slow."
- If you profile and see "long renders," that's the function-call phase being slow - usually due to expensive computations inside the function, not DOM work.
When someone says "this component is slow to render," ask: do they mean the function call is slow, or the DOM updates are slow, or the paint is slow? Each has different causes and different fixes.
Misconception 2: "Render and Paint Are the Same"
Render is React's work. Paint is the browser's work. They're separate.
- Render happens synchronously (until concurrent rendering interrupts it).
- Commit happens synchronously after render.
- Paint happens when the browser decides to - typically immediately after commit, but the browser controls this.
If you set state, you don't see the updated screen until: render runs, commit runs, paint happens. All three are required.
useLayoutEffect (M10.7) fires between commit and paint. That's the gap where you can do work that affects what's about to be painted. useEffect fires after paint. That's why useEffect doesn't block the user from seeing the updated UI.
Misconception 3: "Components Render Only When Their State Changes"
Components also re-render when:
- Their parent re-renders.
- A context they consume changes value.
The cascade rule is the surprising one - covered in detail in 13.2. For now: a parent rendering causes all its children to render, even if their props didn't change.
React.memo can prevent the cascade, but the default is "render the whole tree below." This is one of the most consequential things to internalize about React.
Misconception 4: "Render Is Expensive"
Render is usually cheap. A typical component's function call takes microseconds. The DOM updates in commit are typically also fast for small changes.
The cases where render becomes measurably expensive:
- The function body does heavy work (sorting large arrays, complex calculations).
- The component renders thousands of children (most of those children's renders add up).
- The component is in a hot path that runs many times per second.
For most components, you can re-render them hundreds of times per second without performance issues. The optimization mindset of "I must minimize renders" is usually misplaced. Optimize when measurements show a problem.
Misconception 5: "Render Is Synchronous"
In React 17 and earlier, yes. In React 18+ with concurrent rendering (M10.6), no. A render can be:
- Started, then paused, then resumed later.
- Started, then thrown away, then re-started with different state.
- Interleaved with other work.
This is what useTransition exploits. Components rendered inside a transition can be interrupted; React abandons the in-progress render if more urgent work arrives.
For most rendering - outside transitions - render is still synchronous from start to finish. The concurrent capabilities are opt-in via specific hooks.
A Practical Example: What Happens When You Click a Button
Trace through what actually happens, in order:
function Counter() {
const [count, setCount] = useState(0)
console.log('Counter rendering, count =', count)
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(c => c + 1)}>+</button>
</div>
)
}
The user clicks the button. The sequence:
Step 1: Event fires. The onClick handler runs. It calls setCount(c => c + 1).
Step 2: React schedules a re-render. The setter doesn't immediately re-render. It schedules the render to happen.
Step 3: React renders Counter. React calls Counter(). The console logs "Counter rendering, count = 1". The function executes and returns the new element tree.
Step 4: React reconciles. It diffs the new tree against the previous tree. Only the <p>'s text content changed (from "Count: 0" to "Count: 1"). The button didn't change.
Step 5: React commits. The DOM is mutated - the <p> element's text content is updated. The button is left alone.
Step 6: Browser paints. The updated <p> text appears on screen.
Step 7: useEffect (if any) runs. Asynchronously, after paint.
The whole sequence happens in milliseconds (usually under 16ms to stay within one frame). From the user's perspective, the click appears to immediately update the screen - the staging through render, reconcile, commit, paint is invisible.
But each phase did distinct work. Understanding which phase does what is what lets you reason about bugs and performance.
The "Pure Render" Discipline
A specific principle worth landing. Render is supposed to be pure - the same component function called with the same state and props should produce the same output, with no side effects.
What this means:
- Don't make API calls during render. (Use effects.)
- Don't update state during render. (Use handlers or effects.)
- Don't write to refs during render. (Use handlers or effects.)
- Don't modify external variables during render.
- Don't read changing values that aren't state or props (like Date.now() for display).
The rule: the function body should be a pure transformation from props/state to JSX.
Why the discipline matters:
- Strict Mode double-renders components in development. If render isn't pure, double-render causes visible bugs.
- Concurrent rendering can call render multiple times for the same update. If render isn't pure, the multiple invocations produce inconsistent results.
- React Compiler (M17) memoizes based on the assumption of purity. Non-pure renders break the memoization.
Most components naturally follow this discipline. The exceptions are usually misunderstandings - engineers thinking they need to "do something" during render when actually they need an effect or a handler.
What I Want You to Take From This Lesson
Six things.
One: "Render" specifically means React calling your component function and the function returning JSX. Nothing about the DOM.
Two: The four phases are render, reconcile, commit, paint - each with distinct work and timing.
Three: JSX is just React.createElement calls returning plain objects. Render produces objects, not DOM.
Four: Render is usually fast. Calling a function and creating objects is cheap. Optimize when measurements say to.
Five: Render is supposed to be pure - no side effects, no API calls, no state updates. Side effects go in effects or handlers.
Six: The misconception that render = DOM update produces confused debugging. Knowing the phases lets you ask precise questions about where slowness comes from.
Try It
Exercise: Warmup (15 minutes). Add
console.log('rendering')to a component. Click a button that updates state. Note the order: log fires, then the screen updates.
Exercise: Core (20 minutes). Build a component that logs in render and in useEffect. Trigger a state change. Note the order - render log, then effect log. Effects run after commit, not during render.
Exercise: Core (25 minutes). Add expensive work to a component's render (
for (let i = 0; i < 1_000_000; i++) {}- a busy loop). Profile. Note the render time. Move the work to useEffect. Profile again. Compare.
Exercise: Stretch (30 minutes). Read Mark Erikson's "A (Mostly) Complete Guide to React Rendering Behavior". Note the precise vocabulary it uses. It overlaps with this lesson but goes deeper.
What's Next
Lesson 13.2 covers when components re-render - including the cascade rule, the surprising default that catches every engineer at least once.