AniUI Academy

The Critical Rendering Path

What the browser must finish before the first pixel appears, why render-blocking resources exist, and the concrete techniques that shorten that path.

11 min read

Between "the browser receives HTML" and "a user sees a pixel on screen" is a specific, well-defined sequence of work called the critical rendering path. Understanding it precisely — not just "make the page fast" as a vague goal — is what turns performance work from guesswork into a checklist of concrete, targeted fixes.

The path itself

  1. Step 1

    Parse HTML into the DOM

    The browser reads HTML top to bottom, building the document tree as it goes.

  2. Step 2

    Parse CSS into the CSSOM

    Every stylesheet the browser encounters is fetched and parsed into a tree describing computed styles.

  3. Step 3

    Combine into the render tree

    DOM and CSSOM are merged into the tree of visible elements, with their computed styles — elements with display: none are excluded here.

  4. Step 4

    Layout

    The browser computes the exact size and position of every element in the render tree.

  5. Step 5

    Paint

    Pixels are actually drawn to the screen based on the layout — this is the moment something becomes visible.

Nothing paints until layout finishes, and layout can't start until both the DOM and CSSOM are ready — this is why CSS is render-blocking by default.

The reason this matters practically is that certain resources block this path from proceeding, and knowing which ones — and why — is what tells you where to actually intervene.

What blocks the path, and why

CSS blocks rendering. The browser refuses to paint anything until it knows how every element should look, to avoid a flash of unstyled content. A <link rel="stylesheet"> in the head, especially a large one or one served slowly, directly delays first paint.

Synchronous <script> tags block HTML parsing. A plain script tag with no async or defer stops the DOM from being built further at that exact point in the document — the browser fetches, parses and executes it, then resumes. Placed early in the document, before the content that actually needs to render, this can meaningfully delay everything downstream.

Fonts can cause a second, separate delay — even after paint, custom web fonts loading late can trigger a layout shift (or invisible text, depending on the font-display strategy) as text re-flows once the real font arrives.

The concrete levers

defer and async on scripts. defer downloads the script in parallel with parsing but delays execution until parsing finishes, preserving order relative to other deferred scripts — the standard choice for scripts that need the DOM but don't need to block rendering. async downloads in parallel and executes the moment it's ready, with no ordering guarantee relative to other scripts — appropriate for independent scripts like analytics that don't depend on, or get depended on by, anything else.

Critical CSS inlining. Extracting just the styles needed for the above-the-fold, initial view and inlining them directly in the HTML <head> means the browser doesn't wait on a separate network round-trip for those specific styles — the rest of the stylesheet can load afterward without blocking that first paint.

Resource hints<link rel="preload"> for a resource you know is critical and would otherwise be discovered late (a hero image, a key font), and <link rel="preconnect"> for an origin you're about to fetch from, so the DNS lookup and TLS handshake happen ahead of the actual request.

Minimizing render-blocking resources generally — fewer synchronous stylesheets, moving non-critical CSS to load asynchronously (via a technique like media="print" swapped after load, or a dedicated loader), and keeping whatever must block the path as small as possible.

Why this framing matters in an interview

"Make it faster" is not an actionable answer. "The critical rendering path is blocked by a large synchronous stylesheet and two head-placed scripts with no defer — inline the critical CSS, defer the scripts, and load the rest of the stylesheet asynchronously" is a specific, defensible plan that demonstrates you know exactly which browser mechanism you're targeting and why it moves the needle on first paint specifically — as distinct from other performance metrics like interactivity, covered elsewhere in this stage.

What to remember

  • The critical rendering path is DOM + CSSOM → render tree → layout → paint, and nothing paints until layout can run, which needs both the DOM and CSSOM ready.
  • CSS is render-blocking by default; a plain, un-deferred script is parser-blocking — both delay first paint, for different reasons.
  • defer, async, critical CSS inlining, and resource hints (preload, preconnect) are the concrete levers for shortening this specific path.
  • Diagnosing "what's slow" starts with naming which stage of this path is blocked and by what, not a vague sense that the page feels slow.

Check yourself

3 questions · pass 3/3 to unlock Code-Splitting and Bundle Budgets

up to 50
  1. 1.Why does a <link rel="stylesheet"> in the <head> block the browser from painting anything, by default?

  2. 2.A page has a <script> tag with no defer or async attribute, placed in the <head>. What does this do to the critical rendering path?

  3. 3.What's the practical benefit of inlining a small amount of 'critical' CSS directly in the HTML <head>, with the rest loaded separately?

3 left to answer