The Critical Rendering Path

How bytes become pixels — DOM → CSSOM → Render Tree → Layout → Paint → Composite — and the two resources that block it. The backbone topic that connects resource hints, async/defer, and Core Web Vitals.

1The pipeline, end to end

The Critical Rendering Path is the fixed sequence of steps the browser runs to turn HTML, CSS, and JS into pixels. Memorize it in three movesconstruct the DOM and CSSOM in parallel (with JS able to interrupt the DOM branch to fetch/compile/run), combine them into the render tree, then render through layout → paint → composite. The shape matters: drawing the two branches merging — and showing where JS jams the parser — is what separates a Lead answer from a memorized list.

1Construct — two trees, built in parallel
HTML
DOM
parse HTML → node tree · incremental
built token-by-token
↳ parser hits a <script> JS
fetch
parse + compile
execute
⚠ pauses parsing · waits on pending CSSOM · mutates DOM
CSS
CSSOM
parse CSS → style tree · all-or-nothing
⚠ render-blocking
 DOM + CSSOM converge 
2Combine — merge, keep only what's visible
Render Tree
DOM + CSSOM · visible nodes only (no display:none)
3Render — geometry, pixels, layers
Layout
size + position
Paint
fill pixels → layers
Composite
GPU assembles layers
🖼️

Sources: web.dev — Understand the critical path · MDN — Critical rendering path · MDN — <script> parsing & execution

2The whole interview is here: what blocks the path

Knowing the six boxes is table stakes. The Lead-level insight is which resources stall the pipeline, and why. There are exactly two villains.

CSS is render-blocking

Blocks the Render Tree

The browser won't render anything until the CSSOM is complete. Why? Any later rule could override an earlier one — paint now and you'd risk a flash of wrong styles. So CSS blocks the render tree by design.

JS is parser-blocking

Blocks DOM construction

A plain <script> pauses HTML parsing — the browser must fetch + execute it before continuing, because JS can mutate the DOM. And: a script waits for any pending CSSOM, since it might read computed styles.

One-liner

“Two things stall the page: CSS blocks painting, JS blocks parsing. So two fixes: ship less critical CSS, and get JS off the parser with defer/async.”

That second sentence — “get script out of the parser's way” — is exactly what async and defer do. This is the rapid-fire question right after resource hints, so let's nail it.

3async vs defer — the script-loading lever

<script src="a.js"></script>              // parser STOPS: fetch + run, then resume
<script src="a.js" async></script>        // fetch in parallel, run ASAP, order NOT guaranteed
<script src="a.js" defer></script>        // fetch in parallel, run AFTER parse, IN ORDER
Attribute Download Executes Order kept? Blocks parser?
(none) blocks parser immediately, mid-parse yes yes
async parallel as soon as downloaded no (whoever lands first) only briefly, when it runs
defer parallel after parse, before DOMContentLoaded yes no

Use defer

Default for app scripts

Order-dependent code, your framework bundle, anything that touches the DOM. Runs after the document is parsed, in order — non-blocking and predictable.

Use async

Independent, order-free

Self-contained third-party scripts — analytics, tag managers — that depend on nothing and that nothing depends on. Runs the instant it lands.

The trap they'll set: “We async'd all our scripts for speed and the app broke intermittently.” Of course — async gives no order guarantee, so a script ran before its dependency. async is for independent scripts only; for anything order-dependent, defer.
One-liner

“Both download in parallel. defer = runs after parsing, in order — app code. async = runs whenever it lands, no order — independent tags only. Use async on ordered code and you get flaky bugs.”

One more actor: the preload scanner

Even when a sync script blocks the parser, browsers run a secondary preload scanner that races ahead in the raw HTML and kicks off downloads for images, CSS, and scripts it spots. It's why fetches overlap instead of running strictly one-by-one — and why an inline script that injects resources defeats it (the scanner can't see resources that don't exist in the markup yet).

Sources: MDN — <script> (async/defer) · web.dev — Render-tree, Layout & Paint

4How a Lead optimizes the path

Optimization = shorten the path to first paint. Three concrete levers, all derived from §2:

Full loop

Concept: the CRP is DOM+CSSOM→render tree→layout→paint→composite; CSS blocks render, sync JS blocks the parser. Trade-off: inlining critical CSS speeds first paint but bloats the HTML and is hard to cache — so I automate extraction, not hand-maintain it. Anchor: “On our listing page we inlined ~14KB critical CSS and deferred the rest; FCP dropped a beat.” Impact: first paint feeds LCP, a ranking + conversion lever for the platform. Invite: “On a content page I'd lean SSR + critical CSS; on an app shell I'd weigh it against caching.”

Notice how this chains to Lesson 01: resource hints make the critical bytes arrive sooner; the CRP is what the browser does with them. Next lesson connects the output of this path to the metrics — Core Web Vitals (LCP / INP / CLS).

5Check yourself — scenario quiz

Pick an answer; you get instant feedback. Push-back style, like the round.

1. Why is CSS render-blocking?

2. You have three app scripts where b.js needs a.js, and a separate analytics tag. Best loading strategy?

3. A plain <script> sits between a slow <link rel="stylesheet"> and your content. What actually happens?

They want to hear that JS also waits on CSS.

4. Which animation can run on the compositor alone — no layout, no paint?

5. Best one-sentence framing of the CRP optimization goal?

0 / 5 answered

Try this aloud before our next session: “Walk me from typing a URL to first paint, then point at exactly where CSS and JavaScript can block, and name the one fix for each.” Draw the six boxes; time yourself to 90 seconds.
Good follow-up topics:
“Quiz me out loud, harder” “Layout thrashing / forced reflow?” “How does critical-CSS extraction work?” “Where does hydration fit the path?” “type=module vs defer?”