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>
asyncexecutes as soon as the script is available, without guaranteeing document order.deferexecutes 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
| Change | Typical work |
|---|---|
Geometry such as width or top | Style -> Layout -> Paint -> Composite |
Visual style such as color or box-shadow | Style -> Paint -> Composite |
Compositor-friendly transform or opacity | Style -> 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
deferorasyncaccording 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
| Mistake | Better approach |
|---|---|
| Treating rendering as one strict waterfall | Remember that parsing, fetching and rendering can overlap |
| Saying JavaScript always blocks HTML parsing | Distinguish classic blocking, async, defer and module scripts |
| Assuming every DOM change triggers every stage | Identify whether the change affects style, geometry, paint or composition |
| Calling every visual update a reflow | Use layout for geometry work and paint for drawing work |
| Assuming repaint is always cheap | Measure the affected area and frequency |
| Reading geometry after every style write | Batch reads, then batch writes |
| Promoting every element to its own layer | Use compositor hints selectively because layers consume memory |
Animating width, height, top or left by default | Prefer transform and opacity when they produce the required effect |
| Optimizing CSS selector microseconds first | Profile and fix the largest measured bottleneck |
Official References
- MDN: Critical Rendering Path
- MDN: Script Element
- web.dev: Rendering Performance
- Chrome DevTools: Analyze Runtime Performance
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