Articles

The Critical Rendering Path: How Browsers Draw Pixels

The critical rendering path is the sequence a browser follows from HTML bytes to painted pixels — DOM, CSSOM, render tree, layout, paint.

Takina Takina · · 4 min read
Abstract lines suggesting layered web performance metrics

The critical rendering path is the sequence of steps a browser must complete before it can paint the first pixels of a page: parsing HTML into a DOM, parsing CSS into a CSSOM, combining them into a render tree, computing layout, and painting. Every millisecond spent in this pipeline delays what a visitor actually sees, which is why understanding it is the foundation of front-end performance work.

Step one: building the DOM

The browser reads HTML bytes and tokenizes them into a Document Object Model tree — a structured, in-memory representation of every element and its relationships. Parsing is mostly incremental: the browser can start building the DOM before the whole file has downloaded, which is one reason streaming HTML from the server beats waiting to send a complete document.

The catch is <script> tags. A plain <script src="..."> blocks HTML parsing until the script downloads and executes, because the script might use document.write to inject more markup. The async and defer attributes exist specifically to avoid this: defer downloads in parallel and runs after parsing finishes, async downloads in parallel and runs as soon as it’s ready, potentially interrupting parsing.

Step two: building the CSSOM

In parallel with DOM construction, the browser parses CSS — from <link> tags, <style> blocks, and inline style attributes — into the CSS Object Model, a tree of computed styles. Unlike HTML, CSS parsing is render-blocking by design: the browser can’t know the final appearance of an element until it has seen every rule that might apply to it, including ones later in the cascade that override earlier ones.

This is why CSS in the <head> should be minimal and why tools increasingly recommend inlining critical, above-the-fold styles and deferring the rest. It’s also why understanding the CSS cascade matters for performance, not just correctness — a stylesheet the browser has to re-evaluate repeatedly because of specificity conflicts adds real work to this stage.

Step three: the render tree

The browser combines the DOM and CSSOM into a render tree — a representation of only the elements that will actually be visible, with their computed styles attached. Elements with display: none are excluded entirely (though visibility: hidden elements are included, since they still take up space). This tree is what layout and paint actually operate on.

Step four: layout (reflow)

Layout, sometimes called reflow, walks the render tree and computes the exact position and size of every element in pixels, given the viewport dimensions. This is where a flexbox or grid container resolves how much space each child gets, where percentages resolve against their containing block, and where container queries evaluate the size of their nearest containment context.

Layout is expensive because it can cascade: changing the width of one element can force the browser to recompute the position of every sibling and descendant that follows it. JavaScript that reads a layout property (like offsetHeight) immediately after writing a style forces a synchronous recalculation — a pattern known as “layout thrashing” that’s one of the most common causes of jank in interactive pages.

Step five: paint and composite

Paint fills in the actual pixels — text, colors, borders, shadows — onto layers. Modern browsers then composite those layers together on the GPU, which is why properties like transform and opacity are cheap to animate: they can be handled entirely in the compositing step without triggering layout or paint at all. Animating width, top, or margin, by contrast, forces layout on every frame.

Why this maps directly to Core Web Vitals

Google’s Core Web Vitals are essentially instrumented checkpoints along this same pipeline. Largest Contentful Paint measures how long it takes the biggest above-the-fold element to reach the paint stage. Cumulative Layout Shift measures how much the layout step moves things around after the user has already started looking at the page. A slow critical rendering path shows up directly as a worse score on both.

Practical levers

StageWhat slows it downWhat speeds it up
DOM parsingLarge, deeply nested HTMLStreaming HTML, smaller documents
DOM parsingBlocking <script> tagsdefer/async, moving scripts out of <head>
CSSOM parsingLarge stylesheets, @import chainsMinimal critical CSS, deferred non-critical CSS
LayoutComplex selectors, forced reflowsBatching DOM reads/writes, content-visibility
Paint/compositeAnimating layout-triggering propertiesAnimating transform/opacity only

Frameworks that render on the server, like those using SSR instead of SSG, send a browser HTML that’s closer to its final form, which shortens the DOM-construction and layout work needed before first paint compared to a blank shell hydrated entirely by JavaScript. That tradeoff is also central to how hydration works — the browser still has to attach event listeners and reconcile interactivity after that first paint, which is a separate cost from the rendering path itself but competes with it for the main thread.

The takeaway

The critical rendering path is DOM plus CSSOM plus render tree plus layout plus paint, in that order, and a browser can’t skip a step. Performance work is mostly about shrinking or parallelizing each stage: streaming and deferring HTML and scripts, keeping render-blocking CSS small, avoiding forced synchronous layouts from JavaScript, and preferring compositor-only properties for animation. Once you can name which stage a slow page is stuck in, the fix usually follows.

Takina Takina · · 4 min read

What Is the Beacon API? navigator.sendBeacon()

The Beacon API lets a page send one last async request as it unloads, without blocking navigation or racing the browser's page teardown.

#Web Development #Frontend #Performance
Takina Takina · · 5 min read

CSS will-change Explained: Compositing and Performance

The CSS will-change property hints the browser to prepare an element for an upcoming change, moving it to its own compositor layer. When to use it and when not to.

#CSS #Web Development #Performance