AlgoPlusAlgoPlus
Learn/Web
Lesson

The Critical Rendering Path

How the browser turns HTML and CSS text into pixels: DOM, CSSOM, render tree, layout, paint, composite.

9 min read Watch it move Build it

A page arrives from the server as text, not pixels. A browser can't draw HTML and CSS directly — it runs them through a fixed assembly line called the critical rendering path: parse the HTML into a DOM, parse the CSS into a CSSOM, merge those into a render tree of what's actually visible, compute every box's size and position (layout), fill in the pixels (paint), and stack the layers onto the screen (composite). Understanding this line is what lets you reason about *why* a page appears when it does.

Step 1 — HTML becomes the DOM

The browser reads the HTML top to bottom and builds the Document Object Model: a tree of nodes, one per tag, nested exactly as the markup nests. This is the structure the browser and JavaScript both work with. Parsing is *incremental* — the tree grows as bytes stream in, so the browser can start work before the whole file has arrived.

<html>
  <head>
    <link rel="stylesheet" href="style.css">
  </head>
  <body>
    <h1>Hello</h1>
    <p>Welcome to the web.</p>
  </body>
</html>

That markup becomes a tree: html at the root, with head and body children; body holds an h1 and a p, each wrapping a text node. That tree is the DOM.

Step 2 — CSS becomes the CSSOM

In parallel, every stylesheet is parsed into the CSS Object Model — a matching tree of style rules that says how each element should look. Styles *cascade*: a rule on body also affects the h1 inside it unless something more specific overrides it, so the browser must read the whole stylesheet before it knows any element's final computed style.

CSS is render-blocking
The browser refuses to paint anything until the CSSOM is complete — drawing unstyled content and then restyling would flash ugly. So a large or slow stylesheet holds up the first paint. This is why critical CSS is kept small and delivered early.

Step 3 — the render tree

The DOM and CSSOM are merged into the render tree: only the nodes that will actually be drawn, each paired with its computed style. Invisible nodes are left out — head never renders, and anything with display: none is skipped entirely (note that visibility: hidden still takes up space, so it *stays* in the tree).

Step 4 — layout, paint, composite

  1. 1Layout (reflow) — walk the render tree and compute the exact size and position of every box, in real pixels. This is where percentages and em units resolve against the viewport.
  2. 2Paint — fill in the actual pixels for each box: text, colours, borders, shadows, images. The output is a set of draw commands, often split across layers.
  3. 3Composite — stack the painted layers in the right order and hand them to the GPU to draw on screen. This is the moment you see the page.
Not all changes cost the same
Changing geometry (width, top, font-size) forces a reflow — the most expensive step, because positions must be recomputed. Changing only a colour skips layout and just repaints. Animating transform or opacity can skip both and run purely on the compositor, which is why those are the smooth properties to animate.

Where JavaScript fits

A plain <script> tag is parser-blocking: the browser stops building the DOM to download and run the script, because the script might call document.write or read the DOM. Worse, a script that reads styles must wait for the CSSOM, so a stylesheet above a script can stall parsing too. Adding defer (run after parsing, in order) or async (run whenever it arrives) lets the parser keep going — the standard fix for render-blocking scripts.

OperationTimeSpace
Parse HTML → DOM · incremental as bytes streamO(n) in bytesO(nodes)
Layout (reflow) · the expensive step to avoid re-triggeringO(render-tree size)O(boxes)
Paint + composite · composite of transform/opacity is GPU-cheapO(pixels / layers)O(layers)
Check yourself
Why does a large stylesheet delay the first paint even if the HTML is tiny?