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.
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 → CompositeOperations 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 → CompositeOperations 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.comPractical: 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 reflowPractical: 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 reflowsKey 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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
