Understanding Reflow and Repaint: The Core Logic Behind Front‑End Performance Optimization

Reflow recalculates layout when geometric properties change, while repaint only redraws visual styles; the article explains their triggers, compares performance impact, and provides practical techniques—such as using DocumentFragment, batching reads before writes, and leveraging transform/opacity—to minimize costly reflows and achieve optimal front‑end rendering.

CodeNotes
CodeNotes
CodeNotes
Understanding Reflow and Repaint: The Core Logic Behind Front‑End Performance Optimization

Reflow (Layout)

When an element's geometric properties (position, size) change, the browser must recalculate layout, then paint and composite.

Trigger reflow → Layout recalculation → Paint → Composite

Operations that trigger reflow include:

// Modify geometric properties
element.style.width = '200px';
element.style.height = '100px';
element.style.margin = '10px';
element.style.padding = '20px';
element.style.fontSize = '18px';  // text size affects layout
element.style.display = 'none';   // removes from layout tree

// Add/remove DOM nodes
document.body.appendChild(newElement);

// Read properties (forces immediate layout)
const w = element.offsetWidth;
const h = element.offsetHeight;
const rect = element.getBoundingClientRect();
const style = getComputedStyle(element);

Repaint (Paint)

Changing only visual properties (color, background, shadow) that do not affect geometry causes the browser to repaint without a layout step.

Trigger repaint → Skip Layout → Paint → Composite

Operations that trigger repaint but not reflow:

element.style.color = 'red';
element.style.backgroundColor = '#fff';
element.style.boxShadow = '0 0 10px rgba(0,0,0,0.1)';
element.style.border = '1px solid red';  // note: changing border-width triggers reflow
element.style.visibility = 'hidden';

Is Reflow Always Slower Than Repaint?

Not necessarily, but reflow has a broader impact:

Reflow impact:
  • The element itself
  • All its children
  • Some ancestors (width changes may affect parents)
  • Sibling elements (float changes affect surrounding elements)

Repaint impact:
  • Only the element itself (does not affect other elements' positions)

Composite‑Only Changes Are Most Efficient

// transform and opacity only trigger compositing
element.style.transform = 'translateX(100px)';
element.style.opacity = '0.5';

// Full CSS property trigger reference:
// https://csstriggers.com

Practical: Batch DOM Operations

// ❌ Each iteration triggers a reflow (100 reflows)
for (let i = 0; i < 100; i++) {
  const li = document.createElement('li');
  li.textContent = `Item ${i}`;
  list.appendChild(li);  // triggers reflow
}

// ✅ Option 1: Use DocumentFragment (recommended)
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
  const li = document.createElement('li');
  li.textContent = `Item ${i}`;
  fragment.appendChild(li);  // operates on offline fragment
}
list.appendChild(fragment);  // triggers a single reflow

// ✅ Option 2: Hide, operate, then show
list.style.display = 'none';  // removes from layout tree
// ... massive DOM operations ...
list.style.display = 'block';   // re‑adds, triggers one reflow

Practical: Avoid Forced Synchronous Layout

// ❌ Interleaved reads and writes → each read forces a reflow
function updateWidths(elements) {
  for (const el of elements) {
    const width = el.offsetWidth;          // forces reflow (read)
    el.style.width = (width * 1.5) + 'px'; // write (marks dirty)
  }
}

// ✅ Batch reads first, then writes
function updateWidths(elements) {
  const widths = elements.map(el => el.offsetWidth); // batch read (one reflow)
  elements.forEach((el, i) => {
    el.style.width = (widths[i] * 1.5) + 'px';       // batch write
  });
  // total: only one reflow
}

Browser Rendering Queue Optimization

The browser batches multiple style changes and triggers a single layout at the end of the frame.

// These three changes are merged into one reflow
element.style.width = '100px';
element.style.height = '100px';
element.style.margin = '10px';
// At frame end, a single Layout occurs

// A read operation breaks the batch:
element.style.width = '100px';
const h = element.offsetHeight;  // forces immediate reflow!
element.style.margin = '10px';
// Result: two reflows

Key Takeaways

Batch DOM changes with DocumentFragment or by hiding the container.

Avoid interleaved reads and writes; read all needed values first, then write.

The browser’s render queue merges style changes, but any read (e.g., offsetWidth) forces an immediate flush.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

performancebrowser renderingDOMreflowrepaintcomposite
CodeNotes
Written by

CodeNotes

Discuss code and AI, and document daily life and personal growth.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.