How to Find Forced Reflows in the Performance Panel

This guide is part of Layout Thrashing & Forced Reflow, within Rendering & CSS Performance. A forced reflow happens when JavaScript reads layout information after changing something that invalidated layout. The browser has to stop and compute layout immediately. Chrome's Performance panel detects this and labels it, but the information is spread across the flame chart, the event details and the Bottom-Up view, and the code at fault is often not where the warning first points.

Every forced reflow has two halves: the write that made layout dirty and the read that forced it to be computed. DevTools shows the read directly, with a stack trace. Finding the write takes a little more work, and fixing the problem usually means moving one of the two. This guide walks through recording, reading and fixing them.

Forced reflow investigation workflow Steps from recording an interaction to identifying the read, finding the invalidating write and confirming the fix. Forced reflow investigation workflow Record throttled interaction Find warnings red triangles on Layout Read stack which line read layout Find write what invalidated it Fix + re-record warning gone

Rapid Diagnosis

  • Record with 4x CPU throttling so costs are visible.
  • Look for purple Layout blocks nested inside yellow script blocks in the Main track.
  • Look for red triangles on tasks and Layout events; hover to see "Forced reflow is a likely performance bottleneck".
  • Count Layout events inside one task: many thin ones indicate thrashing.

Root Cause Analysis

1. A write before a read in the same task. Style changes, DOM insertions or class toggles followed by geometry reads.

2. Hidden reads. Helpers and libraries that read layout without it being obvious at the call site.

3. Loops. Repeated write-read pairs multiply the cost.

4. Large invalidation scope. Each forced layout covers much of the page when there is no containment.

Step-by-Step Resolution

1. Record a focused trace

Open DevTools, select the Performance panel, set CPU throttling to 4x, start recording, perform the single interaction, and stop. Keep traces short so the interaction is easy to find. Enable "Enable advanced paint instrumentation" only if you also need paint details; it adds overhead.

2. Read the forced reflow event

Click a Layout event with a red triangle. The Summary tab shows "Forced reflow" with a stack trace: the function that read layout (for example getBoundingClientRect called from positionTooltip at tooltip.js:42). The event details also show "Nodes that need layout" and the layout root, indicating how large the invalidated area was.

3. Find the write that invalidated layout

Look earlier in the same task for the code that changed styles or DOM: in the flame chart, the calls before the forced layout. Recent Chrome versions show an "Invalidations" or "Layout invalidation" section listing what triggered it (with stack traces) when advanced instrumentation is enabled.

javascript
// Example: the read (line 42) is innocent; the write (line 38) made it costly.
function positionTooltip(tip, anchor) {
  tip.classList.add('visible');               // line 38: write — layout dirty
  const r = anchor.getBoundingClientRect();   // line 42: read — forced layout
  tip.style.transform = `translate(${r.left}px, ${r.bottom}px)`;
}
// trade-off: reading the anchor before showing the tooltip avoids the forced
// layout, but the tooltip's own size is then unknown until the next frame.

4. Use Bottom-Up to find the worst offenders

In the Bottom-Up tab, group by Activity and select "Layout"; expand to see which functions caused most layout time. This is useful when there are many small forced reflows spread across code.

Where to look in the Performance panel The parts of the Performance panel that reveal forced reflows and what each shows. Where to look in the Performance panel Main track flame chart Purple Layout nested in script; red triangles Summary tab Forced reflow warning and stack trace of the read Layout event details Nodes needing layout and layout root Invalidations (advanced) The writes that made layout dirty Bottom-Up by activity Total layout time by calling function

Verification

After fixing, record the same interaction under the same throttling. The red triangles should disappear, the number of Layout events within the task should drop to one or zero, and the task duration should decrease. Compare the total time of the interaction in the Interactions track, which approximates INP for that interaction.

Worked Example: A Mega Menu

A retailer's mega menu took 280ms to open on a mid-range phone. The trace showed 48 forced reflows, each about 5ms, inside alignColumns(). The stack traces pointed to offsetHeight reads; earlier in each loop iteration, column.style.height = '' reset the height. The fix split the loop into reset, measure and apply phases, cutting forced reflows to one and the task to 40ms. A second trace then revealed a remaining 25ms layout on the whole document, which contain: content on the menu panel reduced to 6ms. INP p75 for menu opening went from 320ms to 130ms.

Mega menu open: before and after the fix Bar chart of task duration and forced reflow count when opening a mega menu before and after restructuring. Mega menu open: before and after the fix Before (48 forced reflows) 280ms Phases split (1 forced reflow) 40ms + contain: content on panel 21ms

Automating Detection

Manual tracing finds individual problems; automation stops new ones. Puppeteer or Playwright can record a trace during scripted interactions, then scan it for Layout events that occur inside a function call and exceed a threshold. A simpler approach uses the Long Animation Frames API in a test page: interactions whose frames show large forcedStyleAndLayoutDuration values on script entries indicate forced layout. Failing a CI job when a key interaction exceeds a few milliseconds of forced layout catches regressions introduced by new components and library upgrades before they reach users.

javascript
// In a test page: log forced layout time per long animation frame.
new PerformanceObserver((list) => {
  for (const frame of list.getEntries()) {
    for (const s of frame.scripts) {
      if (s.forcedStyleAndLayoutDuration > 5) console.warn('forced layout', s.sourceURL, s.sourceFunctionName, s.forcedStyleAndLayoutDuration);
    }
  }
}).observe({ type: 'long-animation-frame', buffered: true });
// trade-off: only frames over 50ms are reported, so small forced reflows in fast
// frames are missed; use traces for detailed investigation.

Common Mistakes

  • Fixing the read instead of the order. Removing one read often just moves the forced layout to the next read.
  • Recording without throttling. Small costs hide on fast machines.
  • Ignoring library code. Stack traces point into dependencies; the fix may be configuration or replacement.
  • Recording long sessions. Interactions get lost among unrelated activity.

Edge Cases

Forced style recalculation. Reads like getComputedStyle() for non-layout properties force style recalculation without layout; they appear as Recalculate Style inside script.

Iframes. Forced reflows in iframes appear in their own frame tracks; select the right one.

Minified code. Without source maps, stack traces point to minified functions; enable source maps in staging builds.

Extensions. Browser extensions can inject scripts that force layout; profile in a clean profile or incognito window.

FAQ

What does "Forced reflow is a likely performance bottleneck" mean?

Chrome detected a layout computed synchronously because script read layout information while layout was dirty. The stack trace shows where the read happened.

How many forced reflows are acceptable?

One per interaction is common and usually fine. Many in the same task, or a single one on a very large DOM, are worth fixing.

Why does the stack trace point to a library?

The library is reading layout. Check whether your code wrote styles just before calling it, whether the library has options to avoid measuring, or whether a CSS alternative exists.

Can Lighthouse detect forced reflows?

Lighthouse reports long tasks and main-thread work, and newer versions include forced reflow insights in performance traces. DevTools traces remain the most detailed source.

Does Firefox show forced reflows?

Firefox Profiler shows synchronous reflows with stack traces as well, under layout markers. The concepts are the same.

What is the Layout root?

The element from which layout was recomputed. A root of #document means the whole page; a smaller root means containment or the browser limited the work.

Should I profile on real devices?

Yes, for final numbers. Remote debugging an Android phone gives the same Performance panel with real CPU costs, which are often higher than throttled desktop estimates.