How Browsers Load Pages
From URL to pixels: networking, parsing, and rendering
Understanding how a browser turns bytes from the network into pixels on screen helps you diagnose performance problems and write HTML, CSS, and JavaScript that loads fast. The process has several distinct phases, each with its own bottlenecks.
Phase 1 — Network and Navigation
When you type a URL, the browser performs a DNS lookup to resolve the domain to an IP address, opens a TCP connection (with TLS handshake for HTTPS), then sends an HTTP request. The server responds with the HTML document.
URL entered
→ DNS lookup ~20–120 ms
→ TCP connection ~30–100 ms
→ TLS handshake ~60–200 ms (HTTPS)
→ HTTP request 1 RTT
→ First byte (TTFB)
→ HTML bytes arrive
Phase 2 — Parsing HTML and Building the DOM
The HTML parser reads the byte stream and builds the DOM (Document Object Model) tree incrementally. It does not wait for the whole document — it parses and emits nodes as bytes arrive.
- When the parser encounters a
<script>withoutasyncordefer, it stops and executes the script before continuing — this is called a parser-blocking script - External scripts without
async/deferalso block parsing while they download and execute deferscripts download in parallel and execute after HTML parsing finishes
Phase 3 — CSS and the CSSOM
In parallel with HTML parsing, the browser downloads stylesheets and builds the CSSOM (CSS Object Model). JavaScript execution is blocked while the CSSOM is being built — so CSS is render-blocking and also script-blocking.
<!-- These block rendering until downloaded + parsed -->
<link rel="stylesheet" href="styles.css">
<!-- These don't block rendering -->
<link rel="stylesheet" href="print.css" media="print">
<link rel="preload" href="styles.css" as="style" onload="this.rel='stylesheet'">
Phase 4 — Render Tree and Layout
The browser combines the DOM and CSSOM into the render tree — only visible nodes are included (elements with display:none are excluded). Then it calculates the exact size and position of every box on screen — this is layout (also called reflow).
Phase 5 — Paint and Composite
The browser fills in pixels for each element (paint), then combines layers on the GPU (compositing). Properties that only affect compositing — transform, opacity — can animate at 60 fps without triggering layout or paint. Properties like width, margin, or top trigger full layout.
/* Cheap — compositor only */
.animate { transform: translateX(100px); opacity: 0; }
/* Expensive — triggers layout + paint */
.slow { left: 100px; width: 200px; }
Key Performance Metrics
- TTFB (Time to First Byte) — server response time; target < 200 ms
- FCP (First Contentful Paint) — first text or image painted; target < 1.8 s
- LCP (Largest Contentful Paint) — main content visible; target < 2.5 s
- TBT (Total Blocking Time) — long tasks blocking interaction; target < 200 ms
- CLS (Cumulative Layout Shift) — visual stability; target < 0.1
Optimisation Checklist
- Use
deferon non-critical scripts;asyncon independent scripts - Inline critical CSS; lazy-load the rest
- Preconnect to third-party origins early:
<link rel="preconnect"> - Set explicit
widthandheighton images to eliminate CLS - Use
loading="lazy"on below-the-fold images - Minimise layout-triggering JS (avoid reading then writing layout properties in a loop)