awesome-everything RU
↑ Back to the climb

Browser & Frontend Runtime

React Profiler, the Compiler, and production observability

Crux The React DevTools Profiler exposes why each fiber re-rendered and how long it took. The React Compiler automates memoisation. Pairing Profiler with LoAF entries closes the loop from user-reported lag to the exact line of code.
Your altitude — climbing toward senior
ZeroJuniorMiddleSenior
You are at senior altitude — in orbit
◷ 14 min

Users report lag. The app feels fine in the office. The Profiler shows a 38 ms commit triggered by a keystroke in a search box — and 240 ProductCards re-rendering with reason “parent rendered, props unchanged.” That is the bug signature. Without the Profiler, you are guessing; with it, the fix is mechanical.

Reading the React Profiler. The React DevTools Profiler records a commit-by-commit timeline. For each commit it shows which fibers rendered, why each rendered (“props changed”, “hook changed”, “parent rendered”, “context changed”), and how long each took. The senior workflow: record an interaction, find the commit that is too long, sort fibers by self-time, and read the “why did this render” reason.

  • “Parent rendered” on unchanged props → missing bailout. Wrap in React.memo, stabilise the offending prop reference.
  • “Context changed” across many components → unstable context value. Memoise the provider’s value with useMemo.
  • “Hook changed” → a useState or useReducer value that changed. Check if the computation producing it is memoised.
Debug this
log
Commit #47  —  render: 38.2 ms  (1 of 1 committed)

<App>                rendered  — 0.4 ms  — "hook changed"
  <Header>           rendered  — 0.2 ms  — "parent rendered"
  <Sidebar>          rendered  — 0.3 ms  — "parent rendered"
  <ProductGrid>      rendered  — 36.9 ms — "parent rendered"
    <ProductCard> x240  rendered  — 0.15 ms each — "parent rendered"

Why did <App> render?  -> useState (searchTerm) changed
Note: <Header>, <Sidebar>, <ProductGrid> props unchanged

Typing in the search box triggers a 38 ms commit. Header, Sidebar, and all 240 ProductCards re-render with reason 'parent rendered', and the Profiler says their props did not change. What is the fix?

Production observability: LoAF + Profiler. The Profiler gives you local reproduction; Long Animation Frames (LoAF) give you production attribution. When a LoAF entry attributes a slow frame to a React bundle, the Profiler reproduces it locally and names the exact fibers. The two together close the loop from “users report lag” to “this component re-renders 200× because of an inline style object on line 44.”

Profiler → fix workflow

1. Record an interaction in the Profiler

2. Find the longest commit — sort by total render time

3. Read the “why did this render” reason for slow fibers

4. Fix: “parent rendered” + stable props → React.memo; “context changed” → memoise provider value; state down → collocate state

5. Verify — the fixed commit should show only genuinely-changed fibers

React Compiler and the future of memoisation. For a decade, keeping the bailout firing meant scattering useMemo, useCallback, and React.memo by hand — tedious, error-prone, and itself not free (each memo cell costs memory and a comparison). The React Compiler (formerly “React Forget”, stable-track as of React 19) is a build-time tool that analyses component code and inserts the memoisation automatically and correctly, at finer granularity than a human would. It understands which values depend on which inputs and memoises exactly those, so the bailout fires without any hand-written hooks.

The senior takeaway: the mechanism — referential stability gating the bailout — is not going away; it is the foundation the compiler builds on. Understanding why a stable reference lets a fiber skip work is what lets you read the compiler’s output, debug it when it bails out conservatively, and reason about performance whether the memoisation is hand-written or compiler-generated.

Why this works

Why the virtual DOM is not just “overhead.” “Virtual DOM” is the marketing name for the element tree React diffs. It is worth being precise: the virtual DOM is not faster than direct DOM manipulation — a hand-written, optimal sequence of DOM calls always beats React. What the virtual DOM buys is a programming model: you describe the UI as a pure function of state and never write imperative DOM code, and React makes the result fast enough by diffing. The cost is real — building and diffing the element tree is CPU work that a compiler-based framework (Svelte) or a fine-grained reactive one (SolidJS) avoids. React’s bet is that the developer-experience win of “UI = f(state)” is worth the reconciliation overhead, and that time-slicing keeps that overhead from ever blocking the user.

Pick the best fit

A search box filters a 5 000-row table. Typing is janky. Pick the primary fix.

Design challenge

Design the rendering strategy for a real-time trading dashboard: a 2 000-row price grid updating ~10× per second, plus interactive filters and a detail panel. It must keep INP under 200 ms and never drop the input box's responsiveness.

  • Price ticks arrive ~10 times per second from a WebSocket.
  • Only visible rows need to be correct; the grid scrolls.
  • Filter and sort controls must feel instant.
  • The commit phase must stay short — no long uninterruptible blocks.
  • External price store must not tear during concurrent renders.
  • INP target: under 200 ms p75.
Quiz

The React Compiler inserts memoisation automatically. Does understanding useMemo and useCallback still matter?

A fiber is where component state lives between renders — the useState values, the refs, the hook list all hang off the fiber, not the component function. Render after render, the fiber persists and carries that state forward; destroy the fiber (unmount, type change, key change) and the state is gone. The reconciler is, at bottom, a machine for deciding which fibers survive and which are rebuilt — the same statefulness question, of what persists across a transition and what is re-derived, that runs through every layer of the stack. ↻ statefulness

Recall before you leave
  1. 01
    A Profiler trace shows 240 ProductCards re-rendering with reason 'parent rendered, props unchanged'. What does that tell you and what do you do?
  2. 02
    What is a Long Animation Frame (LoAF) and how does it help diagnose React performance problems?
  3. 03
    What is the key insight about the React Compiler that a senior engineer needs to hold onto?
Recap

The React DevTools Profiler records each commit with per-fiber render reasons and durations. “Parent rendered, props unchanged” is the mechanical signature of a missing bailout: add React.memo and stabilise unstable prop references with useMemo/useCallback. “Context changed” across many components points to an unstable context value. In production, Long Animation Frames (LoAF) entries attribute slow frames to React code — pair them with the Profiler to name the exact commit and fibers. The React Compiler (stable-track in React 19) inserts memoisation automatically at build time, eliminating the manual scatter of useMemo/useCallback. But the underlying mechanism — referential stability gating the bailout — remains: the compiler builds on it, and you must understand it to debug the compiler’s conservative decisions and reason about performance across any React codebase.

Connected lessons
appears again in143
Continue the climb ↑React fiber: multiple-choice review
shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources4
expand
  1. 01
  2. 02
  3. 03
  4. 04

Trademarks belong to their respective owners. Editorial reference only.