What Triggers Reflow
Reflow (layout) happens when the browser recalculates the geometry of elements.
Reflow Triggers
// 1. Reading layout properties
element.offsetWidth;
element.offsetHeight;
element.offsetTop;
element.offsetLeft;
element.clientHeight;
element.clientWidth;
element.scrollHeight;
element.scrollWidth;
element.getBoundingClientRect();
window.getComputedStyle(element);
element.getBoundingClientRect();
// 2. Writing layout properties
element.style.width = '100px';
element.style.height = '100px';
element.style.top = '10px';
element.style.left = '10px';
element.style.margin = '10px';
element.style.padding = '10px';
element.style.border = '1px solid black';
element.style.position = 'absolute';
element.style.display = 'block';
element.style.fontSize = '16px';
element.style.float = 'left';
element.style.clear = 'both';
element.style.flexDirection = 'row';
element.style.gridTemplateColumns = '1fr 1fr';
// 3. DOM manipulation
document.body.offsetWidth; // Triggers reflow!
What Doesn't Trigger Reflow
// These only trigger paint, not layout:
element.style.color = 'red';
element.style.backgroundColor = 'blue';
element.style.boxShadow = '0 0 5px black';
element.style.opacity = '0.5';
element.style.visibility = 'hidden';
element.style.transform = 'translateX(100px)';
element.style.backgroundImage = 'url(...)';
element.textContent = 'New text'; // May trigger if width changes
Reflow is Expensive
// Reflow affects:
// 1. The element itself
// 2. Its children
// 3. Its parents
// 4. Its siblings
// 5. Any elements that depend on it
// Example: changing width of body triggers reflow of everything
Layout Thrashing
Layout thrashing occurs when you force the browser to recalculate layout multiple times.
The Problem
// BAD: Alternating reads and writes
const elements = document.querySelectorAll('.item');
// This forces a reflow on EACH iteration!
for (let i = 0; i < elements.length; i++) {
elements[i].style.width = `${elements[i].offsetWidth + 10}px`; // Read + Write
}
Why It's Bad
// 1. Read offsetWidth → triggers layout
// 2. Write style.width → invalidates layout
// 3. Read offsetWidth → triggers NEW layout
// 4. Write style.width → invalidates layout
// ... repeat 1000 times = 1000 layouts!
Solution: Batch Operations
// GOOD: Batch reads, then batch writes
const elements = document.querySelectorAll('.item');
// Read phase
const widths = Array.from(elements).map(el => el.offsetWidth);
// Write phase
for (let i = 0; i < elements.length; i++) {
elements[i].style.width = `${widths[i] + 10}px`;
}
// Single reflow!
More Examples
// BAD
document.body.appendChild(newDiv);
const height = newDiv.offsetHeight; // Forces layout
document.body.appendChild(anotherDiv);
const width = anotherDiv.offsetWidth; // Forces layout
// GOOD
const fragment = document.createDocumentFragment();
fragment.appendChild(newDiv);
fragment.appendChild(anotherDiv);
document.body.appendChild(fragment); // Single reflow
const height = newDiv.offsetHeight; // Read after all writes
const width = anotherDiv.offsetWidth;
// BAD
element.style.width = '100px';
console.log(element.offsetWidth); // Forces layout
console.log(element.offsetHeight); // Forces layout
// GOOD
element.style.width = '100px';
element.style.height = '100px';
// Now read
console.log(element.offsetWidth, element.offsetHeight); // One layout
Detection
// Chrome DevTools: Performance tab
// 1. Record performance
// 2. Look for 'Layout' events
// 3. Check for 'Forced synchronous layout' warning
// Chrome DevTools: Rendering tab
// - Enable 'Layout boundaries' to see elements triggering layout
Optimization
Strategies to minimize reflows in your code.
Use CSS Classes
// BAD: Multiple style changes
element.style.width = '100px';
element.style.height = '100px';
element.style.margin = '10px';
element.style.padding = '5px';
element.style.display = 'block';
// GOOD: Single class change
element.classList.add('expanded');
/* CSS handles the rest */
.expanded {
width: 100px;
height: 100px;
margin: 10px;
padding: 5px;
display: block;
}
Use DocumentFragment
// BAD: Multiple DOM additions
for (let i = 0; i < 100; i++) {
const li = document.createElement('li');
li.textContent = `Item ${i}`;
list.appendChild(li); // Reflow each time!
}
// GOOD: Fragment
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
const li = document.createElement('li');
li.textContent = `Item ${i}`;
fragment.appendChild(li);
}
list.appendChild(fragment); // Single reflow
Avoid Triggering Reflow
// BAD: Reading layout in loop
for (let i = 0; i < 100; i++) {
const width = element.offsetWidth; // Triggers layout!
element.style.width = `${width + 1}px`;
}
// GOOD: Read once
const width = element.offsetWidth;
for (let i = 0; i < 100; i++) {
element.style.width = `${width + i}px`;
}
Use transform for Animations
// BAD: Triggers layout
element.style.left = '100px';
element.style.top = '100px';
// GOOD: Compositor only
inelement.style.transform = 'translate(100px, 100px)';
// Or use CSS animation
.element {
transition: transform 0.3s ease;
}
.element.moved {
transform: translate(100px, 100px);
}
Debounce Resize Handlers
// BAD: Fires many times during resize
window.addEventListener('resize', () => {
recalculateLayout(); // Reflow!
});
// GOOD: Debounce
window.addEventListener('resize', debounce(() => {
recalculateLayout();
}, 100));
Use will-change Wisely
/* Promote to compositor layer */
.animated {
will-change: transform;
}
/* Don't overuse - each layer uses memory */
.animated:hover {
will-change: transform; /* Bad! */
}
Practice Problems
Create a reusable React component implementing Reflow. Include proper state management and accessibility.
Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where neededWrite unit and integration tests for Reflow using React Testing Library.
Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility testsOptimize Reflow for performance. Consider memoization, code splitting, and bundle size.
Solution
// Optimization techniques:
// 1. React.memo / useMemo / useCallback
// 2. Code splitting with lazy()
// 3. Virtual scrolling for lists
// 4. Image lazy loading
// 5. Bundle analysisQuiz
1. What triggers a reflow?
2. What is layout thrashing?
3. How do you avoid layout thrashing?
4. Which property is better for animations?
Flashcards
Question
What triggers reflow?
Click to reveal answer
Answer
Geometry changes: width, height, position, margins, padding, borders, display, font-size, etc.
Question
What is layout thrashing?
Click to reveal answer
Answer
Alternating DOM reads and writes that force the browser to recalculate layout multiple times.
Question
How to avoid layout thrashing?
Click to reveal answer
Answer
Batch all reads together, then all writes. Don't alternate between reading and writing layout properties.
Question
What properties don't trigger reflow?
Click to reveal answer
Answer
color, opacity, visibility, transform, box-shadow, background-image - these only trigger paint.
Question
What is Reflow?
Click to reveal answer
Answer
Reflow is a key concept in frontend development.
Revision Notes
Key Takeaways
- 1.Reflow is triggered by geometry changes (width, height, position)
- 2.Layout thrashing forces multiple reflows - avoid it
- 3.Batch DOM reads and writes to minimize reflows
- 4.Use transform instead of top/left for animations
- 5.CSS classes are more performant than inline styles
Interview Tips
- •Explain what triggers reflow vs paint
- •Show how to identify and fix layout thrashing
- •Demonstrate batching DOM operations
- •Explain why transform is better than top/left for animations
Cheat Sheet
Reflow Cheat Sheet
What is Reflow?
Browser recalculates geometry of elements. Expensive operation.
Reflow Triggers
- Reading: offsetWidth, clientHeight, getBoundingClientRect()
- Writing: width, height, position, margin, padding
- DOM: appendChild, removeChild, offsetWidth read
Layout Thrashing
Alternating reads and writes forces multiple reflows.
Prevention
- Batch reads, then writes
- Use CSS classes instead of inline styles
- Use DocumentFragment for batch updates
- Use transform for animations
- Debounce resize handlers
transform vs top/left
- transform: composite only (fast)
- top/left: triggers layout (slow)