HTMLCritical Rendering Path

Critical Rendering Path

The critical rendering path is the sequence of steps a browser must complete before it can paint the first pixels of a page. Understanding it lets you diagnose why a page feels slow to appear even on a fast connection, and gives you concrete levers to pull to speed it up.
The Five Stages
  • DOM construction — the HTML parser reads bytes and builds the Document Object Model tree.

  • CSSOM construction — the CSS parser reads every stylesheet and builds the CSS Object Model tree.

  • Render tree — the DOM and CSSOM are combined into a tree of only the visible nodes, each with its computed styles.

  • Layout (reflow) — the browser computes the exact size and position of every render-tree node on the page.

  • Paint — the browser fills in pixels: colors, text, images, borders, shadows, onto layers, then composites those layers to the screen.

critical-rendering-path.txt

Text
HTML bytes ──► DOM
                        \
                         ├──► Render Tree ──► Layout ──► Paint ──► Composite
                        /
CSS bytes  ──► CSSOM
1. DOM Construction
The browser parses HTML top to bottom, converting bytes into characters, then tokens, then nodes, then the DOM tree. This is incremental—the browser doesn't wait for the whole document to build the DOM. It's also where render-blocking resources cause delays: if the parser hits a synchronous <script> tag, it must stop and run that script before continuing to build the DOM.
2. CSSOM Construction
Every stylesheet—external <link>, inline <style>, or @import—is parsed into the CSSOM: a tree of rules with their computed specificity and inheritance resolved.
CSS is render-blocking by default
The browser cannot know if a later CSS rule will override an earlier one, so it will not paint anything until the full CSSOM is built. A slow-loading stylesheet in <head> blocks first paint completely, even if the DOM is fully parsed and ready.
3. Render Tree
The browser walks the DOM starting at the root, and for each visible node attaches the matching CSSOM rules. Elements with display: none are excluded entirely—they exist in the DOM but never enter the render tree (unlike visibility: hidden, which is included but invisible).
4. Layout
Also called reflow. The browser calculates the exact pixel geometry—position and size—of every node in the render tree, starting from the viewport's dimensions. This step is recomputed whenever geometry-affecting properties change (resizing the window, adding content, JavaScript reading and writing layout properties in a loop).
5. Paint & Composite
Finally, the browser converts the render tree, with its computed geometry, into actual pixels—text, colors, images, borders, shadows. Modern browsers paint different parts of the page onto separate layers (e.g. a fixed header, a video element) and then composite them together, which lets some properties like transform and opacity animate without triggering a full repaint.
How Render-Blocking Resources Delay First Paint

Two resource types can block the critical rendering path directly:

Resource

Why it blocks

Mitigation

<script> (no async/defer)

Parser stops to download and execute it before building more DOM

Add defer/async, or move to end of <body>

<link rel="stylesheet">

Browser withholds paint until CSSOM is complete

Inline critical CSS, defer non-critical stylesheets

Optimization Techniques
  • Minify HTML, CSS, and JS — fewer bytes to parse and download means every stage starts sooner.

  • Defer or async non-critical scripts — stop them from blocking DOM construction (see the async/defer page).

  • Inline critical CSS — put the small amount of CSS needed for above-the-fold content directly in a <style> tag in <head>, so first paint doesn’t wait on an external stylesheet round-trip.

  • Load remaining CSS asynchronously — for non-critical stylesheets, use a <link rel="preload" as="style" onload="this.rel='stylesheet'"> pattern so it doesn’t block first paint.

  • Reduce CSSOM size — remove unused CSS; a smaller CSSOM is faster to construct and match against.

  • Avoid deeply nested selectors — simpler selectors are cheaper for the browser to match during style calculation.

critical-css-inline.html

HTML
<head>
  <!-- Critical, above-the-fold styles inlined directly -->
  <style>
    body { margin: 0; font-family: system-ui, sans-serif; }
    header { background: #111; color: #fff; padding: 1rem; }
  </style>

  <!-- Full stylesheet loaded without blocking first paint -->
  <link
    rel="preload"
    href="/styles/main.css"
    as="style"
    onload="this.onload=null;this.rel='stylesheet'"
  />
  <noscript><link rel="stylesheet" href="/styles/main.css" /></noscript>
</head>
Measure before optimizing
Chrome DevTools' Performance panel shows the actual time spent in each stage—parsing, style calculation, layout, paint—for a real page load. Optimize the stage that's actually slow rather than guessing.
Reflow is expensive
Any JavaScript that reads a layout property (like offsetHeight) right after writing one (like changing style.width) in a loop forces the browser to run layout synchronously and repeatedly—a pattern known as "layout thrashing." Batch reads and writes separately to avoid it.
Quick Reference

Stage

Input

Output

DOM construction

HTML bytes

DOM tree

CSSOM construction

CSS bytes

CSSOM tree

Render tree

DOM + CSSOM

Tree of visible, styled nodes

Layout

Render tree + viewport size

Exact position/size per node

Paint

Render tree + layout geometry

Pixels on screen