Rendering · stage 04

Reflow and repaint

Changing geometry costs far more than changing colour, and knowing which is which is most of performance work.

Stage 04 of six/Rendering, piece 2 of 3/Full piece/Next: Parsing before the download ends

A close photograph of a screen showing a page mid-layout with rendering boundaries highlighted
Stage 04 · RenderingRepaint — appearance changes only; pixels redrawn, no geometry recalculation

When the browser has to think again

The browser does not draw a page once and rest. Every change to the document — a class toggled, a style property set, a node inserted — potentially triggers work that ripples through the rendering pipeline. That work falls into two distinct categories, and their costs are not remotely similar.

A repaint happens when a pixel's appearance changes but nothing moves. Alter the colour of a button, fade its opacity, change a border's colour: the browser knows exactly which pixels to redraw and it redraws them. Nothing else shifts. The geometry of the page — which element occupies which box, how tall each line is, what the scroll height totals — is untouched. Repaints are comparatively cheap.

A reflow (the term Gecko uses; WebKit calls it layout) is a different order of cost. Change anything that affects geometry — an element's width, height, padding, margin, font size, the content inside it — and the browser must recalculate the positions and dimensions of some or all elements on the page before it can draw anything correctly. That recalculation walks the render tree. In a large document, or one with deeply nested flexbox or grid containers, the traversal is substantial. Then, after layout is finished, the browser still has to repaint the affected region. Reflow always costs at least as much as repaint, and frequently far more.

What triggers what

A few properties that naively look like "just colour" actually force geometry recalculation. box-shadow and outline are painted effects but sit outside the box model — most engines handle them without reflow. border-width, on the other hand, affects layout: changing it forces a reflow. visibility: hidden is a repaint (the element still occupies space); display: none is a reflow (the element leaves the flow entirely).

The geometry properties that reliably force reflow include anything in the box model — width, height, padding, margin, border-width — as well as font-size, line-height, position, float, and overflow. Inserting or removing a DOM node forces reflow too. Reading certain computed values — offsetWidth, offsetHeight, getBoundingClientRect(), scrollTop — does something more insidious: it forces the browser to flush any pending layout work immediately, because the returned value must be correct. If a script reads one of these values inside a loop that also writes geometry properties, it produces a pattern sometimes called layout thrashing, in which the browser is forced to reflow on every iteration rather than batching the work.

The critical rendering path — parsing, style calculation, layout, paint, compositing — is not a single pass. Browsers try to batch changes and resolve them at the end of a frame. The way to exploit that is to make all reads first, then all writes; interleaving them destroys the batching.

A developer's screen showing a waterfall timing chart, bars staggered, photographed straight on
Also in RenderingStylesheets block rendering because nothing can be laid out until the rules are known, and a font can hold text invisible while it loads. The critical path

Compositing: the third tier

Modern browsers introduce a third tier below repaint. Certain properties — transform and opacity being the canonical pair — can be animated entirely on the GPU compositor thread, without touching layout or paint at all. The element's texture is already uploaded; the compositor applies the transform or fades the opacity without involving the main thread. This is why animating transform: translateX() is dramatically cheaper than animating left or margin-left, even though the visual result looks identical. The latter changes geometry and triggers reflow; the former does not.

Browsers promote elements to their own compositor layer when they detect or anticipate these properties. Overusing the hint (the will-change CSS property exists precisely to signal this) bloats GPU memory and can hurt overall performance — compositing layers are not free. The goal is to keep animations and transitions on the compositor path wherever the design permits it.

Stage 04

A few properties that naively look like "just colour" actually force geometry recalculation.

Knowing the cost before you pay it

None of this requires instrumentation to reason about. The principle is architectural: if a change cannot affect where any box sits or how large it is, it is a repaint. If it can, it is a reflow. Properties that bypass both, because they run on the compositor, are a distinct and valuable class. Writing for the specificity of a CSS rule and writing for the cost tier of a CSS property are separate exercises — both belong in the same mental model of what the browser does with the rules it receives.

A network diagram drawn on a whiteboard in marker, partly erased
Stage 04 · Rendering

The stage in a room: changing geometry costs far more than changing colour, and knowing which is which is most of performance work.