Browser Architecture
Understanding how browsers work is fundamental to writing performant web applications. This guide covers the major components and processes involved in turning HTML, CSS, and JavaScript into pixels on screen.
High-Level Architecture
A modern browser consists of several key components:
- User Interface — address bar, back/forward buttons, bookmarks bar, everything visible except the page viewport
- Browser Engine — bridges the UI and the rendering engine, coordinates actions
- Rendering Engine — parses HTML and CSS and renders content to the screen (Blink for Chrome, Gecko for Firefox, WebKit for Safari)
- JavaScript Engine — executes JavaScript code (V8 for Chrome, SpiderMonkey for Firefox, JavaScriptCore for Safari)
- Networking Layer — handles HTTP requests, DNS resolution, and TLS handshakes
- Data Storage — manages cookies, localStorage, IndexedDB, and other persistent storage
Process Architecture
Modern browsers use a multi-process architecture:
- Browser Process — manages the UI, disk, and network access
- Renderer Process — one per tab (or site instance with site isolation), handles parsing, layout, and painting
- GPU Process — handles GPU-accelerated rendering tasks
- Plugin Process — isolates plugins like Flash (legacy) or PDF viewers
- Utility Processes — audio, network service, data decoding
This isolation provides security (a crashed tab doesn’t crash the browser) and stability.
The Rendering Engine
The rendering engine is responsible for displaying the requested content. Its workflow follows a well-defined pipeline.
Parsing HTML to DOM
When the renderer process receives HTML bytes from the network:
- Tokenization — the HTML tokenizer breaks the raw bytes into tokens (start tags, end tags, attributes, text content)
- Tree Construction — tokens are consumed and converted into DOM nodes arranged in a tree structure
- Error Recovery — the HTML parser handles malformed markup gracefully (unclosed tags, misplaced elements) per the HTML specification’s parsing rules
The result is the Document Object Model (DOM) — a tree-structured representation of the page.
Parsing CSS to CSSOM
In parallel, CSS is parsed into the CSS Object Model (CSSOM):
- CSS bytes are converted into tokens
- Tokens become CSSOM nodes
- The browser resolves cascading — specificity, inheritance, and the
!importantflag determine final computed styles
The CSSOM is also tree-structured, mirroring the DOM tree but containing style information.
The Render Tree
The DOM and CSSOM are combined into the render tree:
- Only visible elements are included (elements with
display: noneare excluded;visibility: hiddenelements are included but invisible) - Each node in the render tree contains the computed styles for its corresponding DOM node
- The render tree represents exactly what will be painted on screen
Layout (Reflow)
Once the render tree is built, the browser calculates the geometry and position of every node:
- Each node’s exact coordinates and dimensions are computed
- This is based on the box model — content, padding, border, margin
- Layout calculations traverse the render tree from root to leaves
- Text is broken into lines, and line boxes are created
Layout is also called reflow in some engines.
Painting
After layout, the browser paints pixels to the screen:
- Paint Records — the render tree is traversed and drawing instructions (fill rect, draw text, draw image) are generated
- Rasterization — paint records are converted to actual pixels, often in tiles for GPU compositing
- Compositing — layers are combined in the correct order to produce the final image
The JavaScript Engine
V8 Architecture (Chrome)
V8 is the most widely used JavaScript engine. Its compilation pipeline has two tiers:
- Parser — JavaScript source is parsed into an Abstract Syntax Tree (AST)
- Ignition (Interpreter) — the AST is compiled into bytecode, which Ignition executes. This is fast to start up
- Sparkplug — a non-optimizing compiler that produces machine code from bytecode without type feedback
- TurboFan (Optimizing Compiler) — hot functions (called frequently) are compiled into highly optimized machine code using type feedback collected during Ignition execution
When TurboFan’s assumptions about types become invalid (e.g., a variable that was always a number suddenly becomes a string), the optimized code is deoptimized and execution falls back to Ignition.
Garbage Collection
V8 uses a generational garbage collector:
- Young Generation — newly allocated objects. Collected frequently with a Scavenger (semi-space collector)
- Old Generation — objects that survived multiple young generation collections. Collected with Mark-Sweep-Compact or Incremental Marking
- Orinoco — V8’s concurrent and parallel garbage collector, which minimizes pause times
Memory Management Pitfalls
Common memory leaks in JavaScript:
- Global variables — accidentally creating properties on
window - Event listeners — not removing listeners when elements are destroyed
- Closures — closures retaining references to large objects
- Detached DOM nodes — removing elements from the DOM but keeping references in JavaScript
- Timers —
setIntervalorsetTimeoutcallbacks holding references
The Critical Rendering Path
The Critical Rendering Path (CRP) is the sequence of steps the browser goes through from receiving HTML to rendering pixels. Optimizing the CRP is key to fast page loads.
Steps
- Construct DOM from HTML
- Construct CSSOM from CSS
- Combine into Render Tree
- Layout — compute geometry
- Paint — fill in pixels
CSS is Render-Blocking
CSS blocks rendering. The browser will not paint until the CSSOM is fully constructed. This is why a <link rel="stylesheet"> in the <head> delays the first paint.
Optimization:
- Inline critical CSS
- Use
mediaattributes on stylesheets (media="print"doesn’t block rendering) - Minimize CSS file size
JavaScript is Parser-Blocking
JavaScript blocks HTML parsing by default. When the parser encounters a <script> tag, it pauses parsing, downloads (if external), and executes the script before continuing.
Optimization:
asyncattribute — downloads in parallel, executes as soon as downloaded (blocks parsing during execution)deferattribute — downloads in parallel, executes after HTML parsing is complete, in document ordertype="module"— deferred by default
Preload and Prefetch
<link rel="preload">— tells the browser a resource is needed soon, fetches it with high priority<link rel="prefetch">— tells the browser a resource may be needed for future navigation, fetches with low priority<link rel="preconnect">— establishes early connection (DNS, TLS, TCP) to a required origin
Reflow vs Repaint
These are two distinct operations that affect rendering performance:
Reflow (Layout)
Reflow is the process of recalculating the positions and dimensions of elements. It is triggered when:
- DOM elements are added or removed
- Element dimensions change (width, height, padding, margin, border)
- Content changes (text changes, images with different sizes)
- Browser window is resized
- CSS classes are added or removed
offsetWidth,offsetHeight,getComputedStyle()are read (forces synchronous layout)
Reflow is expensive because it can affect the entire document. A single element’s reflow can trigger reflows of its parents and children.
Repaint
Repaint happens when visual styles change that don’t affect layout:
color,background-color,visibility,box-shadow,border-radius- Outline changes
- Text decoration changes
Repaint is cheaper than reflow because layout doesn’t need to be recalculated, but it still involves repainting the affected region.
Optimizing Rendering Performance
Minimize reflows:
- Batch DOM changes — use
DocumentFragmentor build strings before inserting - Avoid reading layout properties in loops (triggers forced synchronous layout, also called layout thrashing)
- Use
transformandopacityfor animations — these are handled by the compositor and don’t trigger layout or paint - Take elements out of flow with
position: absoluteorposition: fixed— their reflows don’t affect other elements - Use
will-changeto hint to the browser that an element will animate
Minimize repaints:
- Change classes at the highest level possible
- Avoid frequent changes to properties that trigger repaint
- Use CSS containment (
contain: layout style paint) to limit the scope of rendering changes
The Rendering Pipeline
Each frame in the browser follows this pipeline:
- JavaScript — handle events, modify DOM/styles
- Style — recalculate computed styles for affected elements
- Layout — calculate geometry
- Paint — generate paint records
- Composite — combine layers and display
The target is 60 frames per second — each frame has a budget of ~16.6ms. Exceeding this budget causes jank (dropped frames).
Layout Thrashing
Layout thrashing occurs when JavaScript repeatedly forces the browser to recalculate layout:
// BAD — triggers layout on each iteration
for (let i = 0; i < elements.length; i++) {
elements[i].style.width = container.offsetWidth + 'px'; // read forces layout
}
// GOOD — read once, then write
const width = container.offsetWidth;
for (let i = 0; i < elements.length; i++) {
elements[i].style.width = width + 'px';
}
Libraries like FastDOM batch read and write operations to prevent layout thrashing.
Key Interview Points
- The CRP is the single most important concept for page load performance
- CSS blocks rendering; JavaScript blocks parsing
deferandasyncchange script loading behavior —defermaintains order,asyncdoesn’t- Reflow is more expensive than repaint
transformandopacityanimations bypass layout and paint, running on the compositor thread- The browser targets 60fps — 16.6ms per frame budget
- Multi-process architecture provides isolation and stability
- Understanding the rendering pipeline helps identify performance bottlenecks