DOM CSSOM Render Tree
How HTML and CSS become DOM and CSSOM, how the render tree filters display:none, and what blocks construction.
- browser
- dom
- cssom
- render-tree
- critical-rendering-path
Before layout, the browser builds three related structures: the DOM (from HTML), the CSSOM (from CSS), and the render tree (visible boxes that need layout). Misunderstanding them leads to cargo-cult claims like “CSS always blocks JS” without nuance.
Docs: Critical rendering path — MDN, web.dev Constructing the object model.
DOM
HTML bytes → tokenizer → tokens → nodes → Document tree.
<html>
<body>
<div id="app">Hello</div>
<script src="app.js"></script>
</body>
</html>
A classic parser-blocking script without defer/async/type=module pauses HTML parsing until the script downloads and runs — nodes below the script don’t exist yet.
// Safe after parse
document.getElementById('app').textContent = 'Hi';
CSSOM
CSS bytes → parse → CSSOM (style rules the engine can apply). Stylesheets in the document are render-blocking by default: the browser avoids painting unstyled content.
#app {
color: navy;
display: flex;
}
@import chains add serial network delay. Huge unused CSS delays first paint without helping the page.
Render tree
Roughly: combine DOM + CSSOM, skip things that don’t produce boxes (display: none, some metadata), keep visibility-hidden differently (still can affect layout in cases — display:none is the classic omit).
DOM + CSSOM → render tree → layout → paint → composite
Changing class names dirties style recalculation; geometry properties dirty layout; colors dirty paint. See Rendering pipeline.
Why CSS blocks first paint
<head>
<link rel="stylesheet" href="/big.css" />
</head>
Until CSSOM is ready (for that render-blocking sheet), first paint waits. That’s intentional (no FOUC), but critical CSS strategies exist for huge apps — Critical CSS approaches.
JS reading styles
el.classList.add('open');
const h = el.offsetHeight; // may force style+layout now
The engine must flush pending style/layout to answer geometry. Interleaving writes and reads thrash — Avoiding layout thrashing.
Mental model for interviews
| Structure | Input | Output used for |
|---|---|---|
| DOM | HTML (+JS mutations) | Semantics, scripting |
| CSSOM | CSS | Computed styles |
| Render tree | DOM+CSSOM | Layout inputs |
Scripts can mutate DOM anytime; stylesheets block render construction on the critical path of first load — Critical rendering path.
Interview out-loud
“HTML builds the DOM, CSS builds the CSSOM, then the render tree keeps nodes that need boxes. Render-blocking CSS delays first paint; parser-blocking scripts delay DOM construction. Geometry reads can force style and layout flush.”
Mutation after first paint
After first paint, JS can rewrite large DOM subtrees. Each mutation can re-enter style and layout. Batch DOM updates (or use frameworks that do), avoid reading geometry between writes, and keep the render tree shallow for lists via virtualization. DOM/CSSOM construction on first load and mutation cost mid-session are the same pipeline at different times.
Related
Further depth
Teams often under-invest in this topic until an incident or CWV regression. Schedule a one-hour drill: reproduce the failure mode in DevTools, list the top three mitigations for your stack, and file tickets with owners. Revisit after the next major feature that touches networking, rendering, auth, or third parties — those are the moments regressions land. Keep primary documentation links in the runbook so on-call is not searching chat history at 2am.
Concrete artifacts to leave behind: a short architecture note, a CI assertion or header snapshot, and a dashboard panel (lab or field) that would have caught the last bug. Teaching the rest of the team the mental model matters as much as the one-line fix.
Further reading
Related guides
- Critical Rendering PathWhat blocks first paint and LCP on initial load — HTML, CSS, fonts, and scripts — and how to shorten the path without rehashing the full pipeline.
- How Browsers Load a PageEnd-to-end navigation: resolve URL, fetch HTML, discover resources, parse, and get first pixels — without magic.
- BFCache Back Forward CacheHow the back/forward cache freezes pages for instant history nav, what blocks it, and how to restore state safely.
- Browser DevTools Network PanelRead waterfalls, timing phases, headers, throttling, and initiator chains in the Network panel like a production debugger.
- Browser DevTools Performance PanelRecord main-thread timelines: long tasks, style/layout/paint, frames, and how to turn flame charts into INP fixes.