Skip to main content

How Browsers Render (and Where It Janks)

intermediate20 min readLesson 105 of 143

Parse β†’ style β†’ layout β†’ paint β†’ composite. Knowing which step your code triggers tells you what it costs.

Every visual change walks a pipeline. The step that re-runs determines the cost.

The pipeline

  1. Parse β€” HTML becomes the DOM; CSS becomes the CSSOM. Scripts can block parsing (hence defer/async).
  2. Style β€” compute which CSS rules apply to each element (the "Recalculate Style" you see in DevTools).
  3. Layout β€” geometry: where every element sits, how big it is. A change to size or position invalidates layout.
  4. Paint β€” fill pixels: text, colors, shadows, images.
  5. Composite β€” GPU-assembles painted layers. Cheapest of all.

Cost ordering matters: a transform or opacity change can stop at composite (cheap). A width/top/font-size change walks style→layout→paint (expensive — it's on every element it affects, and children). color/background skips layout but still paints.

Jank and the frame budget

Browsers aim for 60fps β€” every frame gets a 16.7ms budget (10ms on 120Hz displays). A script task that runs 200ms freezes that many frames: the page visibly stutters. Long tasks come from big loops, huge DOM mutations, or layout thrashing.

Layout thrashing: the classic performance bug

// BAD: read-write-read-write forces layout every iteration
for (const card of cards) {
  const h = card.offsetHeight; // READ β€” forces layout (previous write is pending)
  card.style.height = h + 20 + "px"; // WRITE β€” invalidates layout
}

The fix is batching: read everything, then write everything.

const heights = cards.map((c) => c.offsetHeight); // all reads
cards.forEach((c, i) => {
  c.style.height = heights[i] + 20 + "px";
}); // all writes

What actually moves the needle

  • visibility/opacity transitions instead of size animations
  • content-visibility: auto for long off-screen sections
  • Fewer, larger DOM mutations (DocumentFragment, one reflow)
  • The Composite-only properties (transform, opacity) for animation

Now practice

Render Pipeline β€” PracticeClassify property costs, batch reads/writes to kill thrashing, and count frames honestly.3 challenges Β· Β· ~18 min