A web page loads in stages: the browser requests HTML, parses it into the DOM, downloads linked resources, builds style information, lays out boxes, paints pixels, and composites layers to the screen.
The page can begin forming before every byte has arrived. That streaming behavior is why ordering matters: a blocking script in the head can delay the first view, while a deferred script can let parsing continue.
Requesting the document
After navigation begins, the browser finds the server and asks for the page document.
The earlier network work may include DNS, connection setup, TLS, redirects, and cache checks. The URL navigation post covers that setup in more detail.
The server usually responds with HTML:
HTTP/2 200
content-type: text/html; charset=utf-8
The browser does not wait for the full document. As HTML bytes arrive, parsing begins.
HTML becomes the DOM
The HTML parser turns text into tokens, then nodes, then a tree.
<main>
<h1>Page title</h1>
<p>Hello</p>
</main>
becomes:
document
└─ html
└─ body
└─ main
├─ h1
└─ p
This tree is the DOM. JavaScript reads and changes it while the page is alive.
HTML parsing is forgiving. Missing tags, table structure, and invalid nesting can be corrected by the parser, so the DOM may not match the original source exactly.
The preload scanner looks ahead
While the parser builds the DOM, the browser also scans ahead for resources such as CSS, JavaScript, fonts, and images.
This preload scanner cannot build the DOM, but it can start downloads early:
<link rel="stylesheet" href="/site.css" />
<script defer src="/app.js"></script>
<img src="/hero.jpg" alt="" />
Starting these requests early reduces idle time. It also means resource hints and correct attributes can change the load sequence before the page is visible.
CSS becomes the CSSOM
CSS is parsed into the CSS Object Model, or CSSOM. The browser uses it to compute which rules apply to each element.
.card {
display: grid;
gap: 1rem;
}
The browser combines the DOM and CSSOM into a render tree. Nodes that do not produce boxes, such as display: none elements, are excluded from that render tree.
CSS can block rendering because the browser needs styles before it can paint the page accurately. Large stylesheets and late CSS can delay the first visible result.
Scripts can block parsing
A normal script blocks the HTML parser:
<script src="/app.js"></script>
The browser pauses parsing, downloads the script if needed, runs it, then resumes. It does this because the script may call document.write() or change elements that affect the rest of parsing.
defer changes the timing:
<script defer src="/app.js"></script>
Deferred scripts download while parsing continues, then run after the document has been parsed.
async scripts also download without blocking parsing, but they run as soon as they are ready. That can be right in the middle of parsing, so use async for independent scripts rather than code that depends on DOM order.
Layout calculates boxes
After the render tree exists, the browser calculates geometry: width, height, position, line breaks, and overflow.
This stage is layout, also called reflow. It depends on viewport size, CSS rules, fonts, image dimensions, and DOM structure.
A missing image size can cause layout shift when the image finally loads. Adding width and height, or using CSS aspect ratio, gives the browser space to reserve before the file arrives.
Paint draws pixels
Paint turns styled boxes into pixels: text, backgrounds, borders, images, and shadows.
The browser records paint work into layers where needed. Some changes require repainting; others can be handled later by compositing.
Changing color may repaint text. Changing width may trigger layout and then paint. Changing transform or opacity can often be handled by the compositor after the element is on its own layer.
Compositing assembles layers
The compositor combines painted layers into the final frame. Scrolling, transforms, and opacity animations can be smooth when they avoid layout and paint work on every frame.
This is why performance advice often recommends animating transform and opacity instead of top, left, width, or height.
The rule is not that GPU work is free. The rule is that layout work is expensive when it repeats during interaction.
Core Web Vitals map to the pipeline
Browser internals show up in user-facing metrics:
- Largest Contentful Paint: when the largest visible text or image finishes rendering
- Cumulative Layout Shift: how much visible content moves after it appears
- Interaction to Next Paint: how long the page takes to respond visually after input
Large images, blocking CSS, long JavaScript tasks, and unstable layout can all hurt these metrics through different parts of the pipeline.
After the first load
The browser keeps working after the page appears. User input, timers, network responses, font swaps, and JavaScript updates can all change the DOM or styles.
Each change may trigger style recalculation, layout, paint, or compositing. The cost depends on what changed and how much of the page depends on it.
A page load is not one event. It is a sequence of decisions about bytes, trees, boxes, and pixels. The earlier you give the browser accurate HTML, CSS, dimensions, and script timing, the sooner it can show stable content.





