The Rendering Pipeline
Date: 2026-08-16
Style, layout, paint, composite — in that order, every frame. Which stage your change enters at decides whether it costs microseconds or milliseconds, and that single fact explains nearly all front-end performance advice.
What it is
The rendering pipeline is the fixed sequence the browser runs to turn the DOM and CSS into pixels on screen.
1 STYLE which CSS rules apply to which element, and what the
computed value of every property is
2 LAYOUT where each box sits and how big it is — geometry.
Also called reflow
3 PAINT filling in pixels: colours, text, borders, shadows,
into one or more layers
4 COMPOSITE assembling the layers in the right order and putting
them on screen. Runs on the GPU
Each stage feeds the next. Entering at an earlier stage forces every stage after it.
The cost hierarchy
This is the payoff, and it’s worth memorising:
| Change | Triggers | Cost |
|---|---|---|
width, height, top, margin, font-size | Layout → Paint → Composite | Expensive |
background-color, color, box-shadow, border-radius | Paint → Composite | Moderate |
transform, opacity | Composite only | Cheap |
In plain terms: moving something with left: 20px makes the browser recalculate where everything is, redraw it, then reassemble it. Moving the same thing with transform: translateX(20px) only changes how an already-drawn layer is positioned when it’s pasted onto the screen. Same visual result, an order of magnitude apart.
This is the entire reason animation advice says to stick to transform and opacity — see Animation Performance.
Layout is the expensive one
Layout is expensive because it’s interdependent: changing one element’s size can change the position of everything after it, and its parent, and its parent’s siblings. The browser often can’t recompute one box in isolation.
Two things make it worse:
- Deep or wide trees. More nodes, more geometry to solve
- Layout thrashing — reading a geometric property after writing one, in a loop, forcing a synchronous recalculation each time. See Reflow and Repaint for the read/write batching fix
The frame budget
At 60Hz the browser has 16.7 milliseconds to produce a frame. Everything — JavaScript, style, layout, paint, composite — has to fit.
16.7ms budget
JS style layout paint composite
├────────┤├──────┤├────────┤├───────┤├─────────┤
↑
yours. everything else is the browser's, and it needs most of it
Realistically your JavaScript has around 8–10ms. Exceed the budget and a frame is dropped, which the user sees as jank. On a 120Hz display the budget halves.
This is also why the main thread matters so much: JavaScript and rendering share it, so a long task doesn’t slow rendering down — it stops it entirely. See Long Tasks and Blocking.
Where it connects
- Compositing and Layers — what gets its own GPU layer, and the memory cost of promoting too much
- The Critical Rendering Path — the first run of this pipeline, from bytes to first paint
- Cumulative Layout Shift — layout running after the user has seen the page, moving things under them
- Interaction to Next Paint — measures exactly this: input to the next completed frame. A blocked pipeline is what the metric detects
Practical
- Animate
transformandopacityonly. Anything else animates through layout or paint every frame - Batch DOM reads and writes rather than interleaving them
- Reserve space for images and late-loading content so layout doesn’t run after paint
- Use DevTools’ Performance panel and read the colour bands — purple is layout, green is paint. If you see purple during an animation, something is entering the pipeline too early
will-changesparingly. It promotes an element to its own layer in advance, which helps once and costs memory if applied broadly