The Critical Rendering Path: How Browsers Turn HTML into Pixels
Every performance tip about "render-blocking resources" or "minimize reflows" refers back to one underlying pipeline. Understanding the pipeline itself makes those tips obvious instead of arbitrary.
Published July 6, 2026A browser doesn't wait for an entire HTML file to arrive before doing anything — it parses HTML incrementally, as bytes arrive, building the Document Object Model, a tree representing every element on the page and how they nest. But the DOM alone isn't enough to draw anything, because it says nothing about size, color, or position; that information comes from CSS.
DOM plus CSSOM equals the render tree
While parsing HTML, the browser also parses any CSS it finds, building a parallel tree called the CSSOM: every rule, resolved against every selector it matches. The browser then combines the DOM and the CSSOM into a render tree, which contains only the nodes that will actually be visible — an element with display: none exists in the DOM but is excluded from the render tree entirely, because it won't be painted and has no box to compute.
Why CSS is render-blocking by default
The browser can't build the render tree until it has the full CSSOM, because any stylesheet rule, including one that arrives near the end of the file, could in principle apply to an element near the top of the page. That's why a <link rel="stylesheet"> in the <head> blocks rendering until it's fully downloaded and parsed — the browser has no safe way to start painting with an incomplete picture of the styles that might still apply. This is also why inlining small, critical CSS directly in the HTML, so the browser doesn't need a separate round trip before it can compute the render tree, is a well-known technique for improving how quickly a page shows anything at all.
Why script tags are worse for blocking than stylesheets
A <script> tag without async or defer blocks HTML parsing itself, not just rendering, because the script might call document.write and inject more HTML, and the browser can't safely continue parsing past that point until it knows whether that will happen. This is the reason scripts are conventionally placed at the end of the body, or loaded with defer (which downloads in parallel but waits to execute until parsing finishes) or async (which executes as soon as it's downloaded, in whatever order that happens, without waiting for parsing to finish at all).
<script src="analytics.js" async></script>
<script src="app.js" defer></script>
Layout and paint: turning boxes into actual pixels
Once the render tree exists, the browser runs layout (sometimes called reflow): computing the exact size and position of every box, given the viewport width and the cascade of CSS rules that apply. Only after layout is complete does paint happen — actually filling in pixels for text, colors, borders, and images, followed by compositing the painted layers together in the right stacking order for the final image sent to the screen.
Why some CSS properties are cheaper to animate than others
Changing a property like width or top forces layout to rerun, because the browser can't know the new size or position of anything without recomputing it, and that layout pass often cascades to neighboring elements too. Changing transform or opacity, by contrast, can usually skip layout and even skip paint entirely, because the compositor can just reposition or fade an already-painted layer using the GPU. This is the concrete, practical reason performance guidance consistently recommends animating transform instead of top/left, and opacity instead of visibility toggles done via size changes: it's the difference between a full layout-paint-composite cycle on every frame and a composite-only step that's dramatically cheaper.
What this means for page structure in practice
Minimizing what blocks the first paint comes down to a short list derived directly from this pipeline: keep the CSS needed for above-the-fold content small and inline or fast to fetch, defer or async non-critical JavaScript so it doesn't block HTML parsing, and avoid deeply nested DOM structures with complex selectors that make CSSOM construction and layout more expensive than they need to be. None of these are arbitrary rules of thumb — each maps directly onto a specific stage of the pipeline described above.