Skip to main content

Web Page Rendering Cycle

The web page rendering cycle is the work a browser performs to turn HTML, CSS and JavaScript into visible pixels. It builds document and style models, calculates geometry, paints visual details and composites layers onto the screen.

HTML -> DOM -----------+
+-> Style -> Layout -> Paint -> Composite
CSS -> CSSOM ---------+
JavaScript can change the DOM or styles

One-line idea: The browser converts content and styles into a visual structure, calculates where everything goes, draws it, and repeats the necessary work whenever the page changes.

Core Concepts​

The Critical Rendering Path​

The critical rendering path is the set of steps needed to produce the first visible page. A simplified version is:

Receive HTML bytes
↓
Parse HTML and discover resources
↓
Build DOM and CSSOM
↓
Calculate styles and layout
↓
Paint and composite pixels

This diagram is a mental model, not a strict one-time waterfall. Browsers stream, parse and fetch resources incrementally, and some work can overlap.

1. HTML Parsing and DOM Creation​

The browser decodes HTML bytes, tokenizes the markup and creates nodes in the Document Object Model (DOM).

<body>
<h1>Hello</h1>
<p>Welcome to the page.</p>
</body>

The resulting structure can be pictured as:

Document
└── body
├── h1
│ └── "Hello"
└── p
└── "Welcome to the page."

The DOM represents document structure and content. JavaScript can read and change its nodes through browser APIs.

HTML parsing is incremental. As the parser discovers stylesheets, scripts, images and fonts, the browser can begin requesting those resources before the whole document has finished parsing.

2. CSS Parsing and CSSOM Creation​

The browser parses CSS into the CSS Object Model (CSSOM). It represents selectors, declarations and the cascade used to determine how elements should look.

h1 {
color: royalblue;
font-size: 2rem;
}
DOM   = What elements and content exist?
CSSOM = Which style rules can apply?

CSS is normally render-blocking because later rules can change earlier results. The browser needs the required CSS before it can render the affected content correctly.

3. JavaScript Execution​

JavaScript can modify content, attributes, classes and inline styles:

const heading = document.querySelector("h1");
heading.textContent = "Dashboard";
heading.classList.add("highlighted");

A classic script without async or defer can block HTML parsing while it is downloaded and executed:

<script src="app.js"></script>

Non-critical scripts can often avoid blocking the parser:

<script src="analytics.js" async></script>
<script src="app.js" defer></script>
  • async executes as soon as the script is available, without guaranteeing document order.
  • defer executes after HTML parsing and preserves order among deferred scripts.
  • Module scripts are deferred by default unless marked async.

Heavy JavaScript can delay rendering or user input because much DOM, style and layout work shares the browser's main thread.

4. Style Calculation and Renderable Structure​

The browser matches CSS selectors against elements, resolves inheritance and the cascade, and computes final styles.

Conceptually, the DOM and computed styles produce the structure that must be rendered:

DOM nodes + computed styles
↓
Renderable boxes

An element with display: none and its descendants do not generate layout boxes. Non-visual document nodes such as metadata also do not appear on screen.

The phrase render tree is useful for learning, although browser engines may use several internal trees and data structures rather than one literal tree.

5. Layout​

During layout, the browser calculates the geometry of rendered elements:

  • width and height
  • position
  • spacing
  • line breaks
  • relationships to surrounding elements
Computed styles + viewport size
↓
Coordinates and dimensions for each box

One element can affect many others. Changing a container's width may change its children's widths, text wrapping and the position of later content.

The older term reflow generally refers to performing layout again after something changes.

6. Paint and Rasterization​

During paint, the browser records how visual parts should be drawn, including:

  • text
  • colors and backgrounds
  • borders
  • shadows
  • images

Those drawing instructions are then rasterized into pixels, often across tiles and layers. Paint determines what each part looks like; layout has already determined where it belongs.

7. Compositing​

The browser may place painted content on separate layers. During compositing, it combines those layers in the correct order to create the final frame.

Background layer
Content layer
Animated overlay layer
↓
Final pixels on screen

Some changes, commonly involving transform or opacity, may be handled mainly during compositing after a layer is available. This can avoid layout and paint, but layer creation and memory still have costs.

Rendering After the Initial Load​

The pipeline does not stop when the page first appears. User input, animations, network responses and JavaScript can request new frames.

JavaScript or CSS change
↓
Style calculation, if needed
↓
Layout, if geometry changed
↓
Paint, if pixels changed
↓
Composite the frame

The browser only repeats stages affected by a change.

Three Common Update Paths​

ChangeTypical work
Geometry such as width or topStyle -> Layout -> Paint -> Composite
Visual style such as color or box-shadowStyle -> Paint -> Composite
Compositor-friendly transform or opacityStyle -> Composite, when conditions allow

These paths are useful approximations. The exact work depends on the browser, property, element, layer state and surrounding page.

Layout vs Paint​

element.style.width = "500px"; // May change geometry and trigger layout
element.style.color = "red"; // Usually needs paint but not layout
element.style.transform = "translateX(20px)"; // May be composited

A repaint is not universally cheap, and a layout is not universally catastrophic. Cost depends on how much of the page is affected, how often the work occurs and the device running it.

Forced Synchronous Layout​

Browsers normally batch style and layout work. Code can accidentally force the browser to calculate layout immediately by writing a style and then reading geometry:

for (const card of cards) {
card.style.width = "200px";
console.log(card.offsetWidth);
}

Each offsetWidth read may require pending style changes to be resolved. Repeating this pattern creates layout thrashing.

Batch reads before writes instead:

const widths = cards.map((card) => card.offsetWidth);

cards.forEach((card, index) => {
card.style.width = `${widths[index] + 20}px`;
});

Animation Frames​

Use requestAnimationFrame() for JavaScript-driven visual updates that should align with the browser's next paint:

function update() {
box.style.transform = `translateX(${position}px)`;
position += 2;

requestAnimationFrame(update);
}

requestAnimationFrame(update);

On a 60 Hz display, a new frame is available roughly every 16.7 ms. The browser and other work also need part of that time, so long JavaScript, layout or paint tasks can cause dropped frames and visible jank.

Practical Performance Patterns​

Reduce Critical Work​

  • Keep critical CSS focused and defer non-critical resources where appropriate.
  • Use defer or async according to a script's ordering requirements.
  • Avoid shipping large amounts of JavaScript needed only after interaction.
  • Reduce unnecessarily deep or large DOM structures.
  • Load images and fonts with appropriate sizing and priorities.

Avoid Unnecessary Rendering Work​

  • Group related DOM updates.
  • Read layout values together, then perform writes together.
  • Avoid animating layout-heavy properties when a transform provides the same effect.
  • Change classes rather than repeatedly setting many inline styles.
  • Virtualize very large lists when only a small region is visible.
  • Reserve space for images and dynamic content to reduce layout shifts.

Measure Before Optimizing​

Use browser developer tools to record a slow interaction or page load. Look for:

  • long JavaScript tasks
  • repeated style recalculation or layout
  • large paint areas
  • unexpected layer creation
  • layout shifts
  • frames that miss their time budget

Optimizing the stage that actually dominates the recording is more reliable than memorizing a list of supposedly expensive properties.

Common Mistakes​

MistakeBetter approach
Treating rendering as one strict waterfallRemember that parsing, fetching and rendering can overlap
Saying JavaScript always blocks HTML parsingDistinguish classic blocking, async, defer and module scripts
Assuming every DOM change triggers every stageIdentify whether the change affects style, geometry, paint or composition
Calling every visual update a reflowUse layout for geometry work and paint for drawing work
Assuming repaint is always cheapMeasure the affected area and frequency
Reading geometry after every style writeBatch reads, then batch writes
Promoting every element to its own layerUse compositor hints selectively because layers consume memory
Animating width, height, top or left by defaultPrefer transform and opacity when they produce the required effect
Optimizing CSS selector microseconds firstProfile and fix the largest measured bottleneck

Official References​

Summary​

The browser parses HTML and CSS, calculates styles and layout, paints visual content and composites layers, repeating only the necessary work as the page changes.

HTML and CSS describe the page
↓
JavaScript may change that description
↓
Browser calculates style and geometry
↓
Visual details are painted and rasterized
↓
Layers are composited into a frame
↓
Future changes repeat only affected stages