Articles

Reflow vs Repaint: How Browsers Redraw Pages

Reflow recalculates layout; repaint just redraws pixels. Why the difference matters for animation performance and how to avoid triggering reflow.

Takina Takina · · 3 min read
A dashboard displaying web performance metrics

Reflow (also called layout) is the browser recalculating the position and size of elements on a page; repaint is the browser redrawing pixels without changing layout. Both cost time on the main thread, but reflow is far more expensive — it can cascade through the entire document, while repaint is contained to the pixels that actually changed.

Understanding which one a given style change triggers is the difference between an animation that runs at 60fps and one that visibly stutters.

What triggers each

The critical rendering path runs, roughly, in this order after a DOM or style change: style recalculation, layout (reflow), paint, and composite. Different CSS properties enter that pipeline at different stages.

Triggers reflow (and therefore repaint and composite too, since layout changes always ripple downstream):

  • Changing width, height, margin, padding, or border width
  • Adding or removing DOM nodes
  • Changing font-size or font family
  • Reading certain layout properties like offsetHeight or getBoundingClientRect() — this forces the browser to flush any pending layout work immediately, a pattern known as “layout thrashing” when it happens repeatedly in a loop
  • Changing the content of a text node
  • Resizing the browser window

Triggers repaint but not reflow:

  • background-color, color, visibility, box-shadow — anything that changes appearance without changing geometry

Triggers neither (composite-only, handled by the GPU without touching the main thread):

  • transform
  • opacity
  • filter, in most modern browsers

That last category is why animation libraries and CSS transition guides push so hard toward transform and opacity. A translateX animation and a left animation can look identical to the eye, but one runs entirely on the compositor thread while the other recalculates layout on every frame.

Why reflow is the expensive one

A single element’s size or position rarely exists in isolation — its siblings, parent, and sometimes the whole document depend on it. Change the height of one box in a normal-flow layout and every element below it has to shift. The browser’s layout engine has to walk some portion of the render tree to recompute affected geometry, and in the worst case that’s the entire page.

Repaint, by contrast, is local. If you change a background color, the browser knows exactly which pixels changed and only redraws those — no need to touch layout for anything else.

Reflow vs repaint vs composite

Reflow (layout)RepaintComposite
What changesElement geometry and positionPixel appearance onlyLayer positioning on the GPU
Triggered bySize, position, DOM structure, fontColor, visibility, shadowtransform, opacity
ThreadMain threadMain threadCompositor thread (often GPU)
Relative costHighestMediumLowest
Cascades toCan affect the whole documentAffected element onlyIsolated to its own layer

Practical ways to avoid unnecessary reflow

  • Batch DOM reads and writes. Interleaving element.style.width = ... with element.offsetWidth reads forces the browser to flush layout on every read. Read everything first, then write everything, or use requestAnimationFrame to schedule writes.
  • Animate transform and opacity instead of top/left/width/height. This is the single highest-leverage change for animation performance, and it pairs well with will-change as a hint — see the guide to will-change for when that hint helps versus backfires.
  • Take elements out of normal flow before animating them, using position: absolute or a dedicated layer, so their movement doesn’t force siblings to reflow.
  • Use content-visibility to skip layout work for off-screen content — see content-visibility explained for how it lets the browser defer rendering entirely for sections that aren’t visible yet.
  • Virtualize long lists. Rendering a fixed window of DOM nodes instead of thousands of rows avoids reflow costs that scale with list size — see virtual scrolling explained.
  • Watch Interaction to Next Paint, not just frame rate, as your real-world signal — see Interaction to Next Paint explained for why input responsiveness is what Core Web Vitals actually measures now.

The takeaway

Reflow recalculates layout and can cascade through the whole document; repaint only redraws pixels; composite-only changes skip the main thread entirely. Reading layout properties in a loop, animating geometric properties, and triggering unnecessary DOM writes are the usual culprits behind janky UI. Prefer transform and opacity for anything that animates, batch your DOM reads and writes, and measure with the browser’s performance panel before assuming you know where the time is going.

The Lycoris Team The Lycoris Team · · 5 min read

What Is Incremental Static Regeneration (ISR)?

ISR lets a static page be rebuilt in the background after deploy, on a timer or on demand, without a full site rebuild. How it works and its tradeoffs.

#Web Development #Performance #Frontend
Takina Takina · · 4 min read

Interaction to Next Paint (INP) Explained

INP measures how long a page takes to visually respond to a click, tap, or keypress. What it counts, how it's scored, and how to bring it down.

#Performance #Web Development #Frontend