Engrain by ByteRush

RCT.11.01 Mid-level· 11 minCore — asked often

Profiling React apps: measure first, then fix the real cause

Use the React DevTools Profiler and the Chrome Performance panel's React tracks to see what rendered, why, and how long it took. Measure before optimizing, and know the usual causes: context, new object props, big lists and expensive renders.

Comes up in: React, Web fundamentals, Debugging

Learn#

Varun memoized 40 components to fix a slow search page. It is still slow, and now harder to read. He never measured which component was slow.

The Profiler in React DevTools records every commit (one update applied to the screen), which components rendered, why, and how long each took. Its flame graph shows the tree with the time each component took. React also has a <Profiler> component that reports the same timings in code:

import { GlobalRegistrator } from "@happy-dom/global-registrator";
GlobalRegistrator.register(); // a DOM for Node; in a browser you already have one
globalThis.IS_REACT_ACT_ENVIRONMENT = true;
const { createElement: h, Profiler, useState, memo, act } = await import("react");
const { createRoot } = await import("react-dom/client");

function SlowList({ items }) {
  const end = performance.now() + 10; while (performance.now() < end) {} // pretend each render costs 10 ms
  return h("ul", null, items.map((i) => h("li", { key: i }, i)));
}
const MemoSlowList = memo(SlowList);
const items = ["Tea", "Rice", "Dal"];
let type;
function Shop({ List, onRender }) {
  const [query, setQuery] = useState("");
  type = () => setQuery((q) => q + "a"); // typing in a search box above the list
  return h("div", null, h("input", { value: query, readOnly: true }),
    h(Profiler, { id: "list", onRender }, h(List, { items })));
}
for (const List of [SlowList, MemoSlowList]) {
  const updates = [];
  const onRender = (id, phase, actualDuration) => { if (phase === "update") updates.push(actualDuration >= 5 ? "slow" : "skipped"); };
  const root = createRoot(document.createElement("div"));
  await act(() => root.render(h(Shop, { List, onRender })));
  for (let i = 0; i < 3; i++) await act(() => type());
  console.log(List === SlowList ? "plain list:" : "memo list: ", updates.join(", "));
  await act(() => root.unmount());
}
This example uses Node.js, so it runs in our checks rather than in your browser.

Output (verified)

plain list: slow, slow, slow
memo list:  skipped, skipped, skipped
Finding the slow component, then proving the fixStep 1 of 4

State

  • recording…

Typing in the search box feels slow. Start recording in the React DevTools Profiler and type three letters.

Read the steps as text
  1. Typing in the search box feels slow. Start recording in the React DevTools Profiler and type three letters.
  2. Three commits appear. The flame graph shows SlowList taking about 10 ms in each, though its items never changed.
  3. "Why did this render?" says: the parent rendered. Props were the same.
  4. Wrap it in memo (props are stable) and record again: SlowList is skipped, and each commit takes well under a millisecond.

Predict#

Predict first

A memo component receives onSelect={() => select(id)} from its parent. Does memo skip its renders?

Interview angle#

Say it like this — about 30 seconds

“I record an interaction in the React DevTools Profiler with “record why each component rendered” turned on. The flame graph shows which components rendered and how long they took, and the reason, such as a parent render, a state change, or a context change. Then I fix the actual cause: split or memoize a context value, stabilize object and function props, move state down, virtualize a big list, or make an expensive render cheaper. React.memo does not help when props change every render, or when the component reads changing context.”

Stays on your device. Nothing is uploaded.
Follow-up How do you find why a React component re-renders?
React DevTools Profiler with “Record why each component rendered”, or the highlight-updates option; then check props, state and context.
Follow-up When does React.memo not help?
When props are new objects or functions each render, when it reads changing context, when it receives new children, or when its render is cheap anyway.
Follow-up What does React 19.2 add to Chrome's Performance panel?
React performance tracks showing scheduler work and component renders alongside the browser’s own timeline.

Practice#

Guided

Analyze recorded profiler data: render counts, the slowest commit and components over budget.

Done when

  • Counts are sorted by renders, then by name.
  • The slowest commit is the one with the largest total time.
  • Components with any single render over budget are listed by name.
Your solution
Ctrl/⌘ + Enter

Remember#

Try to answer each card before you open it. Then add them to your reviews.

What is a commit in the React Profiler?

One update applied to the screen, with the components that rendered in it.

Which setting shows why a component rendered?

"Record why each component rendered while profiling" in React DevTools.

Why does memo fail with onSelect={() => …}?

The inline function is a new reference every render.

Name three common causes of slow React updates.

Changing context values, new object or function props, big unvirtualized lists.

Go deeper

Four layers. Stop at the one your interview needs; each ends with a quick check.

The core ideaEvery round
Measure first, with a realistic device and data. Development builds are slower than production, so confirm big wins with a production profiling build.
Check yourself: Why profile with a production build too?

Development builds are much slower and add extra checks, so timings are inflated.

How it works underneathSenior rounds

<Profiler onRender> receives actualDuration, the time spent rendering the subtree in this commit, and baseDuration, the time a full render would take without memoization Source: React: Profiler.

Check yourself: What does the Profiler component's actualDuration mean?

Time spent rendering the profiled subtree in this commit.

Edge cases and trapsWhere people slip
React work is only part of an interaction. React 19.2 adds React tracks to Chrome’s Performance panel, so you can see components next to layout, paint and long tasks (Performance tracks shipped in React 19.2).
Check yourself: Can a fast React render still cause a slow interaction?

Yes: layout, paint or other JavaScript can dominate; check the Performance panel too.

In productionStaff-level answers

Typical causes: one context holding everything (RCT.03.05), new object or function props, state placed too high, big lists (RCT.11.02), and expensive calculations in render.
Re-profile after each fix Source: React 19.2 release post.

Check yourself: What should you do after fixing a slow render?

Profile again to confirm, and add a check or budget so it does not come back.

Finished the five steps? Mark it, and its cards join your reviews.

Last checked 2026-11-01. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.

Interview questions this answers

  • How do you find why a React component re-renders?
  • When does React.memo not help?