skip to content

Profiling a React screen, one commit in the React DevTools Profiler lasts about 48 ms, yet in that commit's Ranked chart no component's own render time is above 2 ms. What does that pattern tell you, and where do you look next?

level: middleimportance: should knowfreq 44%

answer

  1. do the division first
  2. total is large, each part is tiny
  3. breadth, not depth
  4. count of components is the lever
  5. flamegraph shows how wide the cascade is

basics

~20 s

No single component is slow — the 48 ms is the sum of many cheap renders, so this is a breadth problem, not a hot-component problem. Look at the Flamegraph to see how much of the tree re-rendered and find the highest component in the coloured region.

solid answer

~60 s

Commit duration is the total for the commit; the Ranked chart shows each component's **own** render time. When the total is large and every individual entry is tiny, the arithmetic has already told you the answer: roughly a few hundred components each cost a fraction of a millisecond, and their sum is the 48 ms. There is nothing to optimise inside any one of them. So I switch to the Flamegraph for that commit and look at how much of the tree is coloured rather than greyed — that shows the extent of the cascade. Then I walk to the **highest** coloured component and read its render reason, because that is where the update entered the tree; everything below it typically rendered because its parent did. The useful question becomes "why is this much of the tree in the commit at all", not "which component is slow". The opposite reading applies too: one component at 40 ms of a 48 ms commit is a genuinely expensive component, and that is a code problem in one file.

go deeper

for a junior

Know that a commit's duration and a component's own render time are different numbers, and that the Ranked chart sorts by the second one.

for a middle

Do the arithmetic out loud: a large total with uniformly tiny self times means many components rendered, so the lever is how many rendered rather than how fast any one of them is.

for a senior

Show the follow-through — Flamegraph for the extent of the cascade, the highest coloured component for the entry point, and a re-recording of the same interaction as the evidence that the change helped.

for a principal

Own the question of whether the number matters at all: tie the commit's cost to how often the interaction occurs, and be able to say when the profile is good enough and the team should stop optimising renders.

## Two different numbers The Profiler surfaces cost at two granularities, and confusing them is the classic misreading. - **Commit duration** — the height and label of a bar in the commit chart at the top of the Profiler tab. It is the cost of one batch of work React finished and applied. - **Self time** — what the Ranked chart sorts by. The time spent running one component's own render work, excluding its descendants. A commit's duration is, to a first approximation, the sum of the self times of every component that rendered in it, plus React's own overhead. That relationship is what makes the described profile readable without any further tooling. ## Reading the described profile 48 ms total, no self time above 2 ms. Divide: the time is distributed across a large number of components. That is a **breadth** problem. Concretely it means React ran a great many component functions, reconciled their output, and each one was individually fine. The implications: - Rewriting any single component saves at most 2 ms of 48. Nothing you do inside one component moves this number. - The lever is the **count** — how many components ended up in this commit at all. - The finding lives above the components in the list, at whatever caused that much of the tree to render. ## The contrasting shape It helps to name what you would have done instead: | Profile shape | Reading | Where the fix lives | | --- | --- | --- | | 48 ms commit, top self time 40 ms | One genuinely expensive component | Inside that component's own code | | 48 ms commit, top self time 2 ms | Hundreds of cheap renders | In what caused so much of the tree to render | | 48 ms commit, top self time 12 ms | Mixed | Fix the one component, re-profile, then reassess | Same duration, three different investigations. This is why an interviewer asks the question with numbers rather than in the abstract: they want to see you divide. ## Where you look next 1. **Switch to the Flamegraph for that commit.** The coloured-versus-greyed ratio shows how much of the tree participated. If most of the screen is coloured for what was a small interaction, the scope is the problem. 2. **Walk to the highest coloured component.** The top of the re-rendering region is where the update entered. Everything under it is a consequence. 3. **Read the render reason there** — which requires having recorded with the Profiler setting that captures why each component rendered. The reason on that top component is the actionable one; the reasons underneath it are usually just "the parent rendered". 4. **Look back at the commit bar chart.** Is this one 48 ms commit per interaction, or several? Multiple commits per single user action is its own finding, and the total the user feels is the sum. 5. **Use component filters** so the charts are readable. Hiding host DOM elements in particular removes a large amount of noise from both charts on a wide tree. ## Sanity checks before acting - **Is 48 ms actually bad here?** For a one-off transition it may be invisible; for something happening on every keystroke it is not. The Profiler gives you the number, not the threshold — that comes from how often the commit occurs. - **The numbers come from a development build on your machine.** Use them comparatively: record the same interaction after your change and put the two commit durations side by side, rather than treating 48 ms as a user-facing figure. - **Check the length of the Ranked list itself.** In this shape it *is* the metric — a list of four hundred entries is the finding, stated more directly than any duration. ## The wrong move The failure mode is to open the top entry of the Ranked chart and start optimising it because it happens to be first. In a flat profile the ordering is close to noise; the top row being 2 ms and the tenth being 1.8 ms tells you nothing worth acting on. Read the shape of the distribution before you read its maximum.

  • What would the same 48 ms commit mean if the top Ranked entry were 40 ms?
    That one component is genuinely expensive on its own — it is doing real work in its render, such as heavy computation or building a very large amount of output. The investigation moves into that single file, and the rest of the commit is irrelevant. It is the easier of the two shapes to act on, because the target is unambiguous.
  • Before acting, how do you decide whether 48 ms is even a problem?
    By how often it happens and what the user is doing when it does. A 48 ms commit once, on a navigation, is unremarkable; the same commit on every keystroke means the interface cannot keep up with typing. The commit bar chart answers this directly — count how many of these commits one interaction produces.
  • Why is the length of the Ranked list itself worth looking at?
    Because in a flat profile it is the clearest statement of the problem. Four hundred rows means React ran four hundred component functions for one update, and that count — not any duration in the list — is what you are trying to reduce. It also gives you a crisp before/after measure when you re-record.

saying these in an interview costs you the question

  • Optimises the top Ranked entry just because it is first
  • Confuses commit duration with a single component's self time
  • Concludes React itself is slow rather than counting the renders
  • Treats development-build milliseconds as user-facing numbers
  • Never checks how many commits one interaction produced

context