Parsing HTML and CSS: the DOM, the CSSOM and what blocks them
How the browser turns bytes into the DOM and CSSOM, why CSS blocks rendering and scripts block parsing, what the preload scanner does, and exactly when async, defer and module scripts run.
Comes up in: Web fundamentals, Recruiter screen
Learn#
Karan’s landing page shows a white screen for 3 seconds on 4G, though the HTML arrives in 200 ms. His <head> loads an analytics script and three stylesheets.
The HTML parser reads bytes, splits them into tokens such as start tags and text, and a tree builder turns tokens into the DOM, the tree of nodes JavaScript works with. CSS goes through its own parser into the CSSOM, the browser’s model of every style rule.
The browser will not paint until it has the CSS it knows about: painting first would show unstyled content and then jump. So stylesheets are render-blocking.
A classic <script src> is parser-blocking: the parser stops, fetches and runs it, because the script might call document.write or read the DOM above it.
defer fetches in parallel and runs after parsing, in order. async fetches in parallel and runs as soon as it arrives, in any order. type="module" behaves like defer unless it is also async.
Page
- <head> … ⏸ a.js
Network
- a.js …
Console
empty
The parser builds the DOM from the top. It meets <script src="a.js"> with no attributes and stops: it must fetch and run a.js first.
Read the steps as text
- The parser builds the DOM from the top. It meets <script src="a.js"> with no attributes and stops: it must fetch and run a.js first.
- Meanwhile the preload scanner reads ahead and starts fetching b.js (defer) and c.js (async).
- a.js runs, and parsing continues. c.js finishes downloading and runs at once, pausing the parser briefly.
- Parsing ends. Deferred and module scripts now run in document order, then DOMContentLoaded fires.
Predict#
Predict first
Two deferred scripts: big.js (2 MB) then small.js (2 KB). small.js downloads first. Which runs first?
Deferred scripts wait for parsing to finish, then run in the order they appear. small.js waits for big.js. With async, small.js would run first, which is why async is wrong for scripts that depend on each other.
Interview angle#
Say it like this — about 30 seconds
“The parser turns HTML into the DOM, and CSS becomes the CSSOM. CSS is render-blocking: the browser will not paint until stylesheets load, to avoid a flash of unstyled content. A classic script is parser-blocking, because it might change the document. async downloads in parallel and runs whenever it arrives, in any order. defer downloads in parallel and runs after parsing, in order, before DOMContentLoaded. Module scripts are deferred by default. The preload scanner finds resources early while the parser waits.”
Follow-up What is the difference between async and defer?
Follow-up Why is CSS render-blocking?
Follow-up What does the preload scanner do?
Practice#
Guided
Write a small audit that finds the scripts and stylesheets that block a page’s first render.
Done when
- Plain external scripts are reported;
async,deferand module scripts are not. - Inline scripts are not reported (they have no
srcto fetch). - Print-only stylesheets are not reported as render-blocking.
Remember#
Try to answer each card before you open it. Then add them to your reviews.
When does a defer script run?
After parsing finishes, in document order, before DOMContentLoaded.
When does an async script run?
As soon as it downloads, in any order.
Why is CSS render-blocking?
Painting before CSS loads would flash unstyled content.
What does the preload scanner do?
Reads ahead in the HTML and starts fetching resources while the parser waits.
How does a module script behave by default?
Like defer: fetched in parallel, run after parsing in order.
Go deeper
Four layers. Stop at the one your interview needs; each ends with a quick check.
The core ideaEvery round
Check yourself: What happens when HTML has an unclosed tag?
The parser fixes it using the spec's error-recovery rules; it never stops with an error.
How it works underneathSenior rounds
Check yourself: Why does a script wait for earlier stylesheets?
It might read computed styles, so the browser waits for CSS before running it.
Edge cases and trapsWhere people slip
<link rel="preload"> for those (WEB.01.05).Check yourself: Which resources can the preload scanner not find?
Ones created by JavaScript, or referenced only from CSS, such as background images and fonts.
In productionStaff-level answers
Use defer or modules for app code, async for independent scripts such as analytics, and inline only small critical CSS. media="print" stylesheets do not block screen rendering, which some sites use to load non-critical CSS.
Check yourself: Which scripts should usually be async?
Independent third-party scripts such as analytics.
Finished the five steps? Mark it, and its cards join your reviews.
Last checked 2026-11-01. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.
Interview questions this answers
- What is the difference between
asyncanddefer? - Why is CSS render-blocking?
- What does the preload scanner do?