C.W.K.
Stream
Lesson 06 of 06 · published

Profiling Concurrent React

~11 min · profiling, devtools, performance

Level 0React Novice
0 XP0/54 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
The performance-tuning workflow in 2026 is short: open React DevTools Profiler, record a slow interaction, find the long row, fix it. Then close DevTools and stop. Don't optimize what you haven't measured.

The two-tab workflow

React DevTools has two performance tabs:

  • Profiler — records React renders during a window of time. Shows you which components rendered, how long each took, and why (state change, parent re-render, context change).
  • Components — inspect any component's current props/state/hooks. Useful for debugging the actual render output, not performance.

What to look for

  1. Components rendering when they shouldn't — props haven't changed, but the component re-renders anyway. Either a parent context updated, or a non-stable prop (un-memoized callback) is being passed.
  2. Long render times — one component takes 20ms+. Either expensive computation in render, or rendering too many children. useMemo / virtualization / React.memo + memoized props.
  3. Cascading re-renders — one state change triggers a tree of renders. Look for state placed too high in the tree (lift it down) or context whose value isn't memoized.

The Suspense indicator

The Profiler shows when a component suspended and how long it took to resolve. Long suspensions visible to the user are the easiest win — either preload the data earlier or split the Suspense boundary.

Concurrent-specific debugging

The 'Why did this render?' field in the Profiler is gold for concurrent React. It tells you whether a render was urgent (state update), transition (low-priority), or initial mount. Mismatches between what you intended (transition) and what happened (urgent) tell you a startTransition wrap is missing.

Don't optimize speculatively. Every memoization, every transition, every memo wrapper adds a tiny cost. If the Profiler shows your app is fast enough, you don't need more optimization — you need to ship the next feature. Performance work pays off when you have a measured problem.

Code

Profiler-driven debugging — the three questions·text
When you see slow / janky behavior:

1. Open React DevTools → Profiler.
2. Click Record. Perform the slow interaction. Stop recording.
3. Look at the flame graph.

Q1: Which component is the long bar?
    → That's where the time is going.

Q2: What does 'Why did this render?' say?
    → Tells you the trigger (Props, State, Hook, Parent).

Q3: Is the long bar in an urgent render or a transition?
    → Transitions are OK to be long; urgent renders block the user.

Fix accordingly:
    • Long urgent render → wrap the state update in startTransition.
    • Cascading re-renders → memoize / lift state down.
    • Suspense fallback flashing → wrap parent state change in startTransition.

External links

Exercise

Deliberately slow down a component (add a 30ms busy loop in its render: const t = Date.now(); while (Date.now() - t < 30) {}). Use the Profiler to find it. Fix one of three ways: memoize so it doesn't re-render unnecessarily, wrap the triggering state update in startTransition so the user isn't blocked, or move the slow computation outside render (useMemo or precompute). Compare before/after profiles.
Hint
The busy loop is your stand-in for a real expensive operation. The point is to feel the Profiler workflow on a known-bad component, so you can apply it to real ones.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.