All topics
Performanceadvanced

Reflow, Repaint, and Layout Thrashing

The two expensive rendering steps triggered by DOM/style changes, and the anti-pattern of alternating reads and writes that forces the browser to recalculate layout repeatedly.

Two rendering steps, with very different costs.

Layout thrashing is like repeatedly asking a mover 'how much does the truck weigh right now?' immediately after loading each individual box, forcing them to stop and put the truck on a scale after every single box, instead of letting them load the entire truck first and weigh it just once at the very end.

Key Concepts

1
    reflow (layout)   recompute geometry -- positions and sizes.
                      Expensive; may affect the whole page.
    repaint           redraw pixels without recomputing layout.
                      Cheaper.
2
What triggers which.
    reflow    width, height, padding, margin, font-size,
              adding or removing elements, reading offsetHeight
    repaint   color, background-color, visibility, box-shadow
    neither   transform, opacity -- these run on the compositor
3
That last line is the practical one: animating transform and opacity avoids both steps entirely, which is why every animation guide recommends them over left and top.
transformopacitylefttop
4
Layout thrashing is the anti-pattern.
    // forces a reflow on every iteration
    elements.forEach(el => {
      el.style.height = el.offsetHeight + 10 + 'px';
    });
5
Reading offsetHeight forces the browser to flush pending style changes and recompute layout. Writing invalidates it again. Alternating reads and writes means a full layout per element.
offsetHeight
6
The fix is to batch: read everything, then write everything.
    const heights = elements.map(el => el.offsetHeight);   // all reads
    elements.forEach((el, i) =>
      el.style.height = heights[i] + 10 + 'px');           // all writes
7
One layout instead of a thousand. The code is barely different and the difference is measurable immediately.
8
The properties that force layout when read.
    offsetTop/Left/Width/Height
    clientTop/Left/Width/Height
    scrollTop/Left/Width/Height
    getComputedStyle()
    getBoundingClientRect()
9
Any of these inside a loop that also writes is thrashing.
10
Other reductions.
    build offscreen, insert once   a DocumentFragment
    toggle a class                 one style recalculation, not many
    position: absolute / fixed     removes the element from normal
                                   flow, so its changes affect less
    will-change: transform         promotes to its own layer --
                                   use sparingly, it costs memory
11
How to confirm it. The Performance panel shows purple layout bars. A long sequence of them during one interaction is thrashing, and the call tree names the line that forced each one.
12
What the interviewer is probing.1. "What is layout thrashing?" Probing: the pattern. Stalls: "Slow rendering." Moves up: alternating reads and writes of layout properties in a loop, forcing a synchronous reflow each time instead of one batched at the end.
13
2. "How do you fix it?" Probing: the technique. Stalls: "Use requestAnimationFrame." *Moves up:* batch all reads first, then all writes, so the browser recalculates once.
14
3. "Which properties force a synchronous layout?" Probing: the specifics. Stalls: "All of them." Moves up: offsetTop, offsetHeight, getBoundingClientRect, scrollTop and getComputedStyle — reading them flushes pending changes.
15
4. "Which properties animate without layout?" Probing: the cheap path. Stalls: "Any of them." Moves up: transform and opacity are composited, so they skip layout and paint entirely — animating top or width does not.