Layout thrashing: repeated reflows in one frame and how to stop them
What is happening
The browser tries to recalculate layout once per frame. But if your code writes a style, then reads a geometry property (offsetWidth, getBoundingClientRect), then writes again — in a loop — every read forces a fresh, synchronous layout of the page. A loop over 100 elements can trigger 100 full reflows in one frame.
The cost is proportional to DOM size, so the same code that is fine on a small page destroys INP on a page with thousands of nodes.
How to recognise it
- Presentation delay (rendering work after the handler) dominates the INP breakdown.
- DevTools Performance shows many purple Layout blocks inside one frame, often flagged “Forced reflow”.
- Code alternates reads (offsetTop, clientHeight, getComputedStyle) and writes (style, classList) inside a loop.
How to fix it
- Batch all reads first, then all writes — never interleave them in a loop.
- Move writes into requestAnimationFrame so they land right before the next paint.
- Prefer CSS solutions (classes, transforms, flex/grid) over measuring and positioning elements in JS.
Problem
items.forEach((el) => {
el.style.width = base + "px" // write
const h = el.offsetHeight // read → forced reflow, every iteration
el.style.height = h * 2 + "px" // write
})
Fix
const heights = items.map((el) => el.offsetHeight) // all reads
requestAnimationFrame(() => {
items.forEach((el, i) => { // all writes
el.style.width = base + "px"
el.style.height = heights[i] * 2 + "px"
})
})
Scan your site for free →