Script Valley
Web Performance Fundamentals
CSS Performance and RenderingLesson 6.3

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.

Up next

How to use CSS containment to improve rendering performance

Sign in to track progress

What causes forced synchronous layout and how to avoid it โ€” CSS Performance and Rendering โ€” Web Performance Fundamentals โ€” Script Valley โ€” Script Valley