What is the difference between reflow and repaint in browsers?

HighIntermediateJavascript
Quick Answer

Reflow (layout) recalculates element geometry, while repaint redraws visual styles without changing layout geometry. Learn what triggers each phase, why forced reflow hurts runtime performance, and how to reduce layout thrashing in modern frontend code.

Answer

The core idea

Browsers render in stages. The two most asked stages are:

  • Reflow (layout): recalculate element size/position.
  • Repaint: redraw pixels (colors, shadows, visibility) without changing geometry.

Reflow is usually more expensive because geometry changes can cascade through large parts of the DOM tree.

Stage

What changes

Typical triggers

Relative cost

Reflow (Layout)

Box size/position and document flow

Changing width/height, font-size, DOM insertions/removals

High

Repaint

Visual appearance only

Changing color/background/visibility/outline

Medium

Composite

Layer composition on GPU

Transform/opacity updates on isolated layers

Lower (often)

Reflow can trigger repaint; repaint does not always require reflow.
JAVASCRIPT
const box = document.querySelector('.box');

// Repaint only (no geometry change):
box.style.backgroundColor = 'tomato';

// Reflow + repaint (geometry changes):
box.style.width = '320px';
box.style.padding = '24px';
                  

What makes reflow expensive

Layout changes can invalidate measurements of nearby or ancestor elements. On large pages, one geometry change can force the browser to recalculate positions for many nodes.

This becomes visible as scroll jank, delayed input response, and dropped frames during animations.

Forced synchronous reflow (layout thrashing)

A common performance bug: write styles, then immediately read layout metrics, repeatedly in a loop. Reading layout properties after writes can force the browser to flush layout right away.

JAVASCRIPT
const items = document.querySelectorAll('.item');

// ❌ Layout thrashing: write -> read in each iteration
for (const item of items) {
  item.style.width = item.clientWidth + 10 + 'px'; // read after prior writes
}

// Reads like offsetWidth/clientWidth/getBoundingClientRect
// can trigger forced layout when styles are dirty.
                  
JAVASCRIPT
const items2 = document.querySelectorAll('.item');

// ✅ Better: batch reads first, then batch writes
const widths = Array.from(items2, (el) => el.clientWidth);

requestAnimationFrame(() => {
  items2.forEach((el, i) => {
    el.style.width = widths[i] + 10 + 'px';
  });
});
                  

Goal

Preferred tactic

Why it helps

Avoid layout thrash

Batch reads and writes separately

Prevents repeated forced reflow in loops.

Smooth animations

Animate transform and opacity

Often avoids layout and heavy paint work.

Limit blast radius

Use CSS contain where valid

Reduces how far layout invalidation spreads.

Lower DOM churn

Coalesce DOM updates (fragments/virtual DOM batching)

Fewer layout/paint cycles per interaction.

Most fixes are about reducing unnecessary layout recalculation.

How to diagnose in DevTools

Record a trace in the Performance panel while reproducing jank:

  • Look for frequent Layout events and long Paint tasks.
  • Correlate long frames with JavaScript that reads layout after style writes.
  • Check whether animation steps stay near the 16.7ms/frame budget (60fps target).
CSS
.card {
  /* isolate layout/paint when appropriate */
  contain: layout paint;
}

/* Prefer transform for motion instead of top/left when possible */
.bad-move { top: 20px; left: 20px; }
.good-move { transform: translate(20px, 20px); }
                  

Interview one-liner

Reflow recalculates layout geometry and is generally more expensive; repaint redraws visuals without layout changes. Optimize by minimizing layout invalidations, batching DOM reads/writes, and animating transform/opacity where possible.

Practical scenario
A filter sidebar updates result-card dimensions on every keystroke, causing repeated layout reads and writes that create visible typing lag.


Common pitfalls

      • Reading getBoundingClientRect() right after multiple style mutations.
      • Animating top/left/width for high-frequency transitions.
      • Ignoring trace data and guessing performance bottlenecks.
Trade-off or test tip
Measure before and after in DevTools and RUM. Compare frame times and INP when switching from layout-triggering animations to transform/opacity patterns.

Still so complicated?

Think of reflow as moving furniture around a room (positions change), while repaint is changing wall color (same layout, new appearance). Moving furniture takes more work.

Similar questions
Guides
Preparing for interviews?

Use the relevant interview-question hub first, then move into a concrete study plan before targeted company sets.