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.
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, orborderwidth - Adding or removing DOM nodes
- Changing
font-sizeor font family - Reading certain layout properties like
offsetHeightorgetBoundingClientRect()— 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):
transformopacityfilter, 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) | Repaint | Composite | |
|---|---|---|---|
| What changes | Element geometry and position | Pixel appearance only | Layer positioning on the GPU |
| Triggered by | Size, position, DOM structure, font | Color, visibility, shadow | transform, opacity |
| Thread | Main thread | Main thread | Compositor thread (often GPU) |
| Relative cost | Highest | Medium | Lowest |
| Cascades to | Can affect the whole document | Affected element only | Isolated to its own layer |
Practical ways to avoid unnecessary reflow
- Batch DOM reads and writes. Interleaving
element.style.width = ...withelement.offsetWidthreads forces the browser to flush layout on every read. Read everything first, then write everything, or userequestAnimationFrameto schedule writes. - Animate
transformandopacityinstead oftop/left/width/height. This is the single highest-leverage change for animation performance, and it pairs well withwill-changeas a hint — see the guide towill-changefor when that hint helps versus backfires. - Take elements out of normal flow before animating them, using
position: absoluteor a dedicated layer, so their movement doesn’t force siblings to reflow. - Use
content-visibilityto skip layout work for off-screen content — seecontent-visibilityexplained 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.
Keep reading
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.
Takina · · 4 min read Stale-While-Revalidate: The Caching Strategy Explained
Stale-while-revalidate serves a cached response instantly while fetching a fresh one in the background, so no request waits on the network.
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.