What causes forced synchronous layout and how to avoid it
forced synchronous layout, layout thrashing, read-write batching, offsetHeight, getBoundingClientRect, FastDOM library, requestAnimationFrame batching
Forced Synchronous Layout
Forced synchronous layout (FSL) occurs when JavaScript writes to the DOM and then immediately reads a layout property, forcing the browser to flush its pending layout queue to return an accurate value. Do this in a loop and you create layout thrashing โ one of the worst performance patterns.
// โ Layout thrashing โ forces layout on every iteration
const items = document.querySelectorAll('.item');
items.forEach(item => {
const width = item.offsetWidth; // READ forces pending layout flush
item.style.width = width * 2 + 'px'; // WRITE invalidates layout
});
// โ Batch reads first, then writes
const widths = Array.from(items).map(item => item.offsetWidth); // all READs
items.forEach((item, i) => {
item.style.width = widths[i] * 2 + 'px'; // all WRITEs
});Properties that trigger FSL when read after a write: offsetWidth, offsetHeight, getBoundingClientRect(), scrollTop, clientWidth, getComputedStyle().
The DevTools Performance panel flags FSL with a red triangle on the Layout record. Look for alternating purple (layout) and yellow (script) bars โ that's thrashing.
// Wrap FSL-prone code in rAF to batch inside a single frame
requestAnimationFrame(() => {
const measurements = items.map(el => el.getBoundingClientRect());
items.forEach((el, i) => applyLayout(el, measurements[i]));
});The FastDOM library enforces read/write batching API-level to prevent FSL by design in larger codebases.
