Browser Rendering
How the browser turns HTML, CSS, and JavaScript into the pixels you see
When the HTML arrives, the browser's real work begins: parsing it, applying styles, computing layout, and painting pixels. Knowing this pipeline explains why some code causes jank and how to keep pages smooth.
The Critical Rendering Path
The browser transforms bytes into pixels through a pipeline:
HTML → DOM
↘
Render Tree → Layout → Paint → Composite
↗
CSS → CSSOM
- Parse HTML → DOM — the HTML becomes the Document Object Model, a tree of nodes.
- Parse CSS → CSSOM — stylesheets become the CSS Object Model, a tree of styles.
- Render tree — DOM + CSSOM combine into only the visible nodes with their styles.
- Layout (reflow) — compute the exact size and position of every box.
- Paint — fill in pixels: text, colors, borders, shadows.
- Composite — assemble painted layers into the final image on screen.
DOM and CSSOM
The DOM is the browser's live, in-memory representation of your HTML — the same tree your JavaScript manipulates with document.querySelector. The CSSOM is the parallel structure for styles. Neither is the raw text file; both are structured trees the browser builds and updates.
The browser won't paint until it has the CSSOM — otherwise you'd see a flash of unstyled content. That's why large or slow stylesheets delay the first paint, and why critical CSS is often inlined.
Layout vs Paint vs Composite
These three stages have very different costs, which matters enormously for performance:
Layout recompute geometry of boxes (expensive — can cascade)
Paint fill pixels for changed areas (medium)
Composite move existing layers on the GPU (cheap — very fast)
Changing a property like width or top forces layout (a reflow) — the browser recomputes positions, potentially for many elements. But animating transform or opacity only needs compositing, which the GPU does cheaply without touching layout or paint.
Animating left or width triggers layout on every
frame and causes jank. Animating transform: translateX() or
opacity stays on the compositor — smooth 60fps. This one rule
fixes most janky animations.
How JavaScript Fits In
By default, a <script> tag blocks HTML parsing — the browser stops building the DOM to download and run it. That's why script placement and the defer/async attributes matter:
<script src="app.js"> blocks parsing until downloaded + run
<script src="app.js" defer> downloads in parallel, runs after HTML is parsed
<script src="app.js" async> downloads in parallel, runs as soon as ready
Use defer for scripts that need the full DOM (most app code); async for independent scripts (analytics). Both let the HTML keep parsing instead of stalling.