Browser Rendering: What happens when you type a URL?
1. Introduction: The Magic Behind the Screen
Have you ever wondered what happens between hitting "Enter" in the address bar and seeing the website appear? It feels instantaneous, but inside the browser, a complex pipeline of operations is executing. This pipeline is called the Critical Rendering Path (CRP).
Understanding CRP is not just for browser engineers. It is the single most important knowledge for Frontend Developers who want to optimize performance. Knowing why an animation stutters (Jank) or why a page loads slowly often comes down to understanding how the browser paints pixels.
Let's dissect the 5 stages of rendering: Parsing, Render Tree, Layout, Paint, and Composite.
2. Step 1: Parsing (HTML & CSS)
When the server sends index.html, it's just a stream of 0s and 1s (Bytes).
The browser engine (Blink, WebKit, Gecko) converts them:
- Bytes -> Characters (
<,d,i,v,>) - Tokens (
StartTag: div) - Nodes (The
divelement object) - DOM Tree (The hierarchy:
body->div->span)
The Preload Scanner
While the main HTML parser buids the DOM tree, it gets blocked by synchronous scripts (<script src="...">).
To prevent wasting time, modern browsers use a Preload Scanner.
This background process peeks ahead in the HTML stream to find resources like images, stylesheets, and scripts and starts downloading them immediately.
This parallelization is why putting vital resources early in the HTML helps performance.
The CSSOM (CSS Object Model)
While parsing HTML, if the browser sees <link rel="stylesheet">, it pauses to fetch the CSS.
Just like DOM, CSS is parsed into a tree structure called CSSOM.
It handles the "Cascading" logic. If div inherits color from body, the CSSOM calculates that final computed style.
The browser cannot render anything until both DOM and CSSOM are ready. This is why CSS is called a "Render Blocking Resource".
3. Step 2: Render Tree Construction
Now we have DOM (Content) and CSSOM (Style). We combine them to create the Render Tree. The Render Tree contains only what is visible on the screen.
display: none vs visibility: hidden
This distinction is crucial for understanding the Render Tree.
display: none: The element is removed from the Render Tree. It does not exist for the renderer. Zero rendering cost.visibility: hidden: The element is in the Render Tree. It is invisible, but it still takes up empty space (layout calculation needed).opacity: 0: Also in the Render Tree.
Using display: none triggers a reconstruction of the Render Tree, while opacity updates usually happen at the Paint or Composite stage.
4. Step 3: Layout (Reflow)
The browser now knows what to draw, but not where. Layout is the process where the browser calculates the precise position and size of each node in Pixels.
- Inputs: "Width: 50%", "Margin: 10px", "Font-size: 2em".
- Context: The Viewport size (e.g., iPhone screen vs Desktop monitor).
- Process: Traversing the Render Tree from root to leaf.
Reflow vs Repaint
They are often confused but are very different.
| Trigger | Layout (Reflow) | Paint (Repaint) | Composite Only |
|---|---|---|---|
| Properties | width, height, left | color, background | transform, opacity |
| Cost | Expensive (CPU) | Moderate (CPU/GPU) | Cheap (GPU) |
| Scope | Whole Document (often) | Element Only | Texture Only |
Reflow is the most expensive operation. It triggers a chain reaction. If you change the width of a <body>, it ripples down to every single child element.
The Layout Thrashing Trap
Running JavaScript that reads layout properties can force the browser to perform a synchronous Reflow.
// Bad Code: Layout Thrashing
const list = document.getElementById('list');
for (let i = 0; i < 100; i++) {
// Reading offsetWidth forces the browser to calculate layout immediately
const width = list.offsetWidth;
list.style.width = (width + 10) + 'px'; // Writing style invalidates layout
}
This "Read-Write-Read-Write" pattern forces the browser to re-calculate layout 100 times.
Fix: Read the value once outside the loop, or use requestAnimationFrame.
5. Step 4: Paint (Repaint)
Now the browser has the geometry. It's time to fill the pixels. Paint involves drawing text, colors, images, borders, and shadows. The browser doesn't confirm everything to one bitmap. It creates multiple Layers.
For example, video elements, canvas, or elements with certain CSS properties (will-change, 3D transform, position: fixed) are promoted to their own layers.
This allows the browser to repaint only that specific layer when it changes, instead of repainting the whole page.
6. Step 5: Composite
This is the final step. All the painted layers are sent to the GPU (Graphics Processing Unit). The GPU flattens these layers into one final image that you see on the screen. This is Composite.
Why Hardware Acceleration Matters
Operations handled by the GPU (Composite) are extremely fast and power-efficient. Operations handled by the CPU (Layout, Paint) are slow.
The Golden Rule of Animation: Prefer modifying properties that only trigger Composite, and avoid properties that trigger Layout.
- Bad for 60fps:
width,height,top,left,margin. (Triggers Layout -> Paint -> Composite). The CPU has to recalculate the geometry of the element AND its neighbors. - Good for 60fps:
transform,opacity. (Triggers only Composite). The GPU just moves the texture.
Next time you move a sidebar, use transform: translateX(100px) instead of left: 100px. The visual result is the same, but the performance difference is night and day.
RequestAnimationFrame vs SetTimeout
For smooth animations, always use requestAnimationFrame(callback) instead of setTimeout.
setTimeout(fn, 16): "Try to run this after 16ms." If the main thread is busy, it will be delayed. It is not synced with the display refresh rate.requestAnimationFrame(fn): "Run this right before the next repaint." The browser optimizes this to run exactly at the monitor's refresh rate (e.g., 60Hz or 144Hz). It guarantees the smoothest possible animation loop.
7. Deep Dive: CSS-in-JS Runtime Costs
Modern frameworks like React often use CSS-in-JS libraries (Generic styled-components, Emotion).
While convenient (scoped styles, dynamic props), they come with a Runtime Cost.
- Parsing: The JS library must parse your template strings into CSS.
- Insertion: It injects a
<style>tag into the DOM. - Recalculation: This forces the browser to re-run the CSSOM construction and potentially Layout.
Compared to standard .css files or utility-first frameworks like Tailwind CSS, CSS-in-JS solutions can delay the Time to Interactive (TTI), especially on mobile devices with slower CPUs.
This is why the industry is shifting towards Zero-runtime CSS solutions (like Vanilla Extract or Tailwind) for critical applications.
8. Summary: Optimizing the CRP
To make your website fast, you need to help the browser process these steps quickly:
- Minimize Bytes: Minify HTML/CSS/JS. Use Gzip/Brotli.
- Optimize Parsing: Defer non-critical JS. Don't block DOM construction.
- Reduce Reflows: Avoid changing layout properties in loops. Use
requestAnimationFrame. - Promote Layers: Use
will-change: transformfor moving elements to hint the browser to use GPU.
Understanding underlying engine mechanics distinguishes a "Coder" from an "Engineer". When you see a stuttering webpage, don't just blame the network. Look at the Rendering Path.