What’s the difference between Repaint and Reflow (often confused with Rework)
Reflow (layout): recalculating element geometry — positions and sizes. Repaint: redrawing pixels (colors, visibility) with geometry unchanged. Reflow is more expensive and triggers a repaint; repaint alone doesn't trigger reflow. ('Rework' isn't a real browser term.) Composite-only props (transform/opacity) skip both.
("Rework" isn't a browser term — the two real concepts are reflow and repaint, and the third tier is composite.)
Reflow (a.k.a. Layout)
The browser recalculates the geometry of elements — their position and size — and where everything sits in the layout. It's triggered by anything that changes layout:
- Changing
width,height,margin,padding,border,top/left,display,font-size. - Adding/removing/resizing DOM nodes.
- Changing the window size.
- Reading layout properties (
offsetHeight,getBoundingClientRect,scrollTop) — can force a synchronous reflow.
Reflow is expensive — it can cascade: changing one element's size shifts its siblings, its parent, its children. And a reflow always triggers a repaint afterward (new geometry must be redrawn).
Repaint
The browser redraws pixels — but geometry stays the same. Triggered by visual-only changes:
color,background-color,visibility,box-shadow,outline,border-color.
A repaint is cheaper than a reflow because the layout is already known — it's "fill in different pixels in the same boxes." A repaint does NOT trigger a reflow.
The hierarchy (cost, cheap → expensive)
Composite only < Repaint < Reflow (+ repaint)- Composite-only —
transformandopacitycan be handled by the GPU at the composite stage, skipping both layout and paint. That's why they're the properties you animate. - Repaint — paint stage only.
- Reflow — layout stage, then paint, then composite. The full pipeline.
Optimizing
- Animate
transform/opacity, nottop/left/width/height. - Batch DOM reads and writes — interleaving them ("read, write, read, write") causes layout thrashing: each read forces a synchronous reflow to get a fresh value. Read all, then write all.
- Minimize layout-affecting changes in hot paths; change a class once instead of many inline styles.
- Use
will-changesparingly to hint the browser to promote an element to its own layer.
The framing
"Reflow — or layout — is the browser recalculating element geometry: positions and sizes. Repaint is redrawing pixels when geometry hasn't changed — like a color change. Reflow is more expensive and always forces a repaint after it; a repaint alone never causes a reflow. Below both is composite-only: transform and opacity go straight to the GPU, skipping layout and paint entirely — which is why you animate those. The classic mistake that triggers excess reflow is layout thrashing: interleaving DOM reads and writes so each read forces a synchronous layout."
Follow-up questions
- •Why does reading offsetHeight force a reflow?
- •What is layout thrashing and how do you avoid it?
- •Why are transform and opacity cheap to animate?
- •Does a repaint ever trigger a reflow?
Common mistakes
- •Thinking repaint and reflow are the same cost.
- •Animating top/left/width instead of transform.
- •Interleaving DOM reads and writes (layout thrashing).
- •Believing a repaint triggers a reflow (it doesn't).
- •Overusing will-change, which can hurt by creating too many layers.
Performance considerations
- •Reflow is the costliest rendering operation and cascades; repaint is cheaper; composite-only updates are cheapest. Batching reads/writes and animating transform/opacity keeps work off the layout and paint stages — the key to smooth 60fps UIs.
Edge cases
- •Reading a layout property right after a write — forced synchronous reflow.
- •A reflow cascading up and down the tree.
- •Off-screen / display:none elements — changes don't reflow until shown.
- •Animating a layout property at 60fps — jank.
Real-world examples
- •Animating a sidebar with transform: translateX instead of left for jank-free motion.
- •Fixing a slow list by batching all DOM reads before all writes.