Browser Rendering Example

Written by Backbone Tutorials Team

Last updated: June 2026 · 8 min read

Reading a layout value such as an element width forces the browser to make sure layout is current. If you read, then write, then read again in a loop, you force that recalculation over and over, a problem called layout thrashing. This example reproduces the slow pattern and then fixes it by separating all reads from all writes, with timings that show the cost.

For the pipeline this exploits, see the browser rendering pipeline and render-blocking resources.

How the browser rendering example batches reads and writesReading all layout values first and then writing all changes avoids forcing the browser to recalculate between each step. read all measurements then write all changes one layout pass, not many

What this example builds

The example resizes a set of boxes based on their current width, written two ways: one that interleaves reads and writes, and one that separates them.

The behaviour you will see

The interleaved version reads each box width and immediately sets a new width, forcing a layout recalculation on every element. The batched version reads all widths first, then applies all new widths, so the browser recalculates layout only once. The second is dramatically faster on many elements.

The thrashing pattern

This loop looks innocent but forces layout on every iteration.

Read, write, repeat

var boxes = document.querySelectorAll(".box");
boxes.forEach(function (box) {
  var w = box.offsetWidth;        // read forces layout
  box.style.width = (w + 10) + "px"; // write invalidates it
});

Batching reads and writes

Splitting the loop into a read phase and a write phase removes the forced recalculation.

All reads, then all writes

var boxes = document.querySelectorAll(".box");
var widths = [];
boxes.forEach(function (box) { widths.push(box.offsetWidth); }); // read phase
boxes.forEach(function (box, i) {
  box.style.width = (widths[i] + 10) + "px"; // write phase
});

Why it is faster

The speed difference comes from how the browser schedules layout.

Forced synchronous layout

When you change the DOM, the browser marks layout as dirty but defers recalculating it. Reading a geometric property like offsetWidth cannot wait, so it forces an immediate recalculation. Interleaving a write and a read repeatedly forces that recalculation each time. Doing all reads while layout is clean, then all writes, lets the browser recalculate once at the end. The full sequence is described in the browser rendering pipeline.

Measuring the difference

The browser timer makes the cost visible.

Timing both versions

console.time("batched");
// run the batched version
console.timeEnd("batched");

On a few hundred elements the batched version completes in a fraction of the interleaved time, and a profiler shows far fewer layout events. The same batching idea underpins the list rendering in the rendering example.

Common pitfalls

Layout thrashing hides in ordinary-looking code.

What usually goes wrong

The most common is reading a layout property inside a loop that also writes, the pattern above. The second is reading properties like offsetTop, scrollHeight, or getComputedStyle without realising they force layout. The third is animating layout-affecting properties such as width or top, when transforming with transform avoids layout entirely. The optimization principles are in render optimization.

Frequently Asked Questions

What is layout thrashing?

Layout thrashing is forcing the browser to recalculate layout repeatedly by interleaving DOM reads and writes. Reading a geometric property after a write forces an immediate recalculation, so a read-write loop pays that cost every iteration.

How do I fix layout thrashing?

Separate reads from writes. Read all the layout values you need first while layout is clean, then apply all the changes. The browser recalculates layout once at the end instead of on every element.

Which properties force layout?

Geometric reads such as offsetWidth, offsetTop, scrollHeight, getBoundingClientRect, and getComputedStyle force the browser to bring layout up to date. Reading them right after a DOM change triggers a synchronous recalculation.

Why animate with transform instead of width or top?

Changing width or top affects layout, so the browser must recalculate positions. The transform property is handled later in the pipeline and can skip layout, making transform-based animations much cheaper.

Read next: the JavaScript SEO example, or back to the Examples hub.

Want the pipeline behind it?

The browser rendering pipeline guide explains layout, paint, and composite.

Read: Browser Rendering Pipeline →