Digital Technology & Websites

Lazy Loading and the Critical Rendering Path Explained

How browsers turn code into pixels, and how lazy loading, critical CSS and deferred scripts speed up pages, with rules on what to lazy-load and what not.

Pages feel fast when the browser can show useful content early. Two techniques help most: shortening the critical rendering path, so styles and scripts do not block the first paint, and lazy loading, so images and frames below the visible screen are fetched only when needed. Both are easy to get wrong. The main rule is to lazy-load what is off-screen and prioritise what is in view.

How a browser builds a page

MDN’s critical rendering path guide describes the sequence the browser follows to turn HTML, CSS and JavaScript into pixels:

  1. DOM: the browser parses HTML into the Document Object Model, incrementally.
  2. CSSOM: it parses CSS into the CSS Object Model. This step is not incremental, because later rules can override earlier ones.
  3. Render tree: the DOM and CSSOM are combined. Only visible content is included, so elements with display: none are left out.
  4. Layout: the browser calculates the size and position of each element.
  5. Paint: pixels are drawn to the screen.

Two consequences follow. First, CSS is render-blocking: MDN says the browser blocks page rendering until it has received and processed all the CSS, because rules can be overwritten. Second, JavaScript can halt HTML parsing while it is fetched and run, since it may change the DOM. MDN recommends marking non-critical scripts as async or deferred, and suggests three strategies: minimise critical resources, reduce the number and size of requests, and prioritise critical assets.

It also advises measuring before optimising, noting for example that selector specificity has only microsecond-level effects and is likely not your lowest hanging fruit.

Lazy loading: what to defer

Native lazy loading uses the loading="lazy" attribute. web.dev’s guide gives the rules:

  • Use it only for images outside the initial viewport. The browser cannot lazy-load an image until it knows where the image sits on the page.
  • Do not lazy-load images likely to be visible when the page loads, especially the largest contentful paint (LCP) image. Leave those at the default eager, and use fetchpriority="high" for an LCP image.
  • Set width and height on every img so space is reserved and layout does not shift. web.dev notes that without dimensions, images default to 0 by 0 and the browser may load many of them at once.
  • The browser starts fetching before an image reaches the viewport, and the distance thresholds are fixed rather than developer-controlled.
  • Browsers that do not support the attribute ignore it. It works on img elements and iframes, but not on CSS background images.
<!-- Above the fold: do not lazy-load -->
<img src="hero.jpg" width="1200" height="630" fetchpriority="high" alt="..." />

<!-- Below the fold: lazy-load -->
<img src="gallery-1.jpg" width="600" height="400" loading="lazy" alt="..." />

The most common mistake is adding loading="lazy" to every image automatically, including the hero. That can delay the largest element on screen and worsen the metric you were trying to improve. Learn what that metric means in our Core Web Vitals guide.

Critical CSS: powerful but optional

Because CSS blocks rendering, one technique is to inline the rules needed for the first screen in a style block in the head, and load the rest of the stylesheet without blocking. web.dev’s guide on deferring non-critical CSS describes the steps: find critical rules (the DevTools Coverage tool can help), inline them, and load the full stylesheet asynchronously, with a noscript fallback. In its demo, First Contentful Paint dropped from 1 second to 0.8 seconds.

The same guide is candid about the trade-offs. It calls this an advanced technique that may lead to bugs if not implemented properly, says most sites can meet recommended performance targets without it, notes that critical CSS must be re-extracted as styles change, and warns that an inline onload handler may conflict with a Content Security Policy. For many sites, simply reducing CSS size, removing unused rules and using media attributes for print styles is safer.

A practical order of operations

Step Action Risk
1 Measure the page with real tools and identify the LCP element Low
2 Compress and resize images; use modern formats Low
3 Defer or async non-critical scripts Medium: test functionality
4 Lazy-load below-the-fold images and iframes Low if the hero is excluded
5 Reduce CSS and remove unused rules Low to medium
6 Consider critical CSS if tools show render-blocking CSS is the bottleneck Higher: needs maintenance

Choosing the right image format is a related step; see image formats compared. If your page relies on heavy scripts for animation or widgets, the trade-offs described in interactive websites apply, and the framework you build on affects how much of this is handled for you, as discussed in Astro vs WordPress.

Limitations

  • Lazy loading helps pages with many images, and does little for a page with a few small ones.
  • A lazy-loaded image can appear late if a visitor scrolls quickly on a slow connection, so keep placeholders and dimensions in place.
  • Search engines must still be able to see content. Do not hide important text behind scroll or click events that crawlers do not trigger; test with Google’s URL Inspection Tool.
  • Techniques interact. Change one thing at a time and compare results.

Where Radiance Impact fits

Radiance Impact’s Technology division builds business websites and landing pages and can review rendering and loading behaviour as part of a redesign or maintenance work. See Technology or contact us.

Sources