Feedback

Defer Offscreen Images

By Christian Schreiber ยท Updated

When PageSpeed Insights or Lighthouse tells you to defer offscreen images, it has found images below the fold that load as soon as the page opens, even though the visitor only sees them after scrolling. The fix is lazy loading: the browser fetches each image only when it gets close to the viewport, which leaves the network free for the content that is visible first.

What the defer offscreen images audit measures

By default, a browser requests every image it finds in the HTML, whether it is visible or not. Lighthouse loads the page in a fixed viewport, identifies images that sit outside that viewport when the page has loaded, and flags the ones that were fetched eagerly instead of deferred. For each image it lists the URL, the resource size and the estimated savings in bytes and time on the initial load. The audit id is offscreen-images. Depending on the Lighthouse version, the wording and the way image advice is grouped in the report can differ.

Deferring does not remove images. It only changes when they are requested.

Why offscreen images slow down LCP and Speed Index

During the initial load, all requests share the same bandwidth and a limited number of connections. Every offscreen image that loads eagerly competes with the resources the visitor needs right now, most importantly your stylesheet, your fonts and the element that becomes the Largest Contentful Paint.

Freeing that bandwidth helps the visible area render sooner, which shows up in LCP and in Speed Index, the lab metric for how quickly the viewport fills with content. It also saves data for images nobody scrolls to. Lazy loading works together with properly sized images and next-gen formats: those two make each image smaller, while deferring decides when it loads.

How to defer offscreen images

1. Find the images Lighthouse flags

Start with the list in the audit itself. It shows which files are offscreen and how large they are. In Chrome DevTools, open the Network panel, filter by "Img", reload and do not scroll. Every image requested before you scroll that you cannot see on screen is a candidate.

2. Add native lazy loading to img and iframe

All modern browsers support the loading attribute. It needs no JavaScript and no library:

<img src="/gallery-item-3.jpg" loading="lazy"
     width="800" height="600" alt="Gallery item">

<iframe src="https://www.youtube-nocookie.com/embed/VIDEO_ID"
        loading="lazy" width="560" height="315"
        title="Product video"></iframe>

The browser starts fetching a lazy image once it is within a certain distance of the viewport. The distance depends on the browser and the connection, so the image is usually ready when the visitor arrives. Iframes deserve the same treatment: embedded maps, video players and social widgets are often heavier than any single image because they pull in their own scripts and styles.

3. Never lazy-load the LCP or hero image

This is the one mistake that turns the fix into a regression. Adding loading="lazy" to the hero image or the main product photo tells the browser to wait with exactly the resource that should arrive first, and LCP gets worse. Load images above the fold eagerly, which is the default, and give the LCP image a higher priority:

<img src="/hero.webp" fetchpriority="high"
     width="1600" height="900" alt="Spring collection">

Only the one image that becomes the LCP element should get fetchpriority="high". Marking several images as high priority cancels the effect.

4. Set width and height to avoid layout shifts

A lazy image that arrives without reserved space pushes the content below it down, which counts toward Cumulative Layout Shift. Always set width and height on the <img>, or reserve the space in CSS with aspect-ratio. In responsive layouts, add height: auto in your CSS so the image scales without distorting.

5. Defer CSS background images with IntersectionObserver

The loading attribute only applies to <img> and <iframe>. Background images set in CSS load as soon as a matching rule applies. To defer them, leave the background out of the initial CSS and add it with a class once the element approaches the viewport:

<div class="promo lazy-bg"></div>

<style>
  .promo.is-visible { background-image: url("/promo-banner.webp"); }
</style>

<script>
  const observer = new IntersectionObserver((entries) => {
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        entry.target.classList.add('is-visible');
        observer.unobserve(entry.target);
      }
    });
  }, { rootMargin: '200px 0px' });

  document.querySelectorAll('.lazy-bg').forEach((el) => observer.observe(el));
</script>

The rootMargin starts loading shortly before the element scrolls into view, which avoids a visible pop-in.

6. Prefer native lazy loading over JavaScript libraries

Before native support existed, lazy loading meant a library that replaced data-src with src as the visitor scrolled. That approach still works, but it has drawbacks. The real image URL is hidden from the browser's preload scanner, images depend on a script that has to download and run first, and visitors without JavaScript see no images at all. Native loading="lazy" avoids all of this. Keep a library only for cases native loading does not cover, such as background images or older components you cannot change yet. Many gallery and carousel components already support lazy loading, so check their options first.

Lazy loading in WordPress, Shopify and Next.js

WordPress

WordPress core adds loading="lazy" to content images and iframes automatically, and current versions skip the first content image so the likely LCP image is not deferred. Recent versions also add fetchpriority="high" to the image they consider the LCP candidate. Problems usually come from elsewhere: themes that output the hero image outside the content, page builders that set their own attributes, and performance plugins that add a second lazy-loading layer on top of core. If you use a plugin for lazy loading, make sure only one mechanism is active and exclude the hero image or the first slider image explicitly.

Shopify

In Shopify themes you control the attribute in Liquid. The image_tag filter accepts a loading parameter, and a common pattern is to load images in the first section eagerly and defer the rest:

{%- if section.index > 1 -%}
  {{ product.featured_image | image_url: width: 800 | image_tag: loading: 'lazy' }}
{%- else -%}
  {{ product.featured_image | image_url: width: 1600 | image_tag: fetchpriority: 'high' }}
{%- endif -%}

Check sliders and collection grids in particular, since third-party apps often inject their own image markup that ignores the theme's logic.

Next.js

The next/image component lazy-loads by default and requires dimensions or fill, which covers most of this audit. The risk is the opposite: the hero image is lazy too unless you mark it. Add the priority prop to the LCP image so it loads eagerly with high priority:

import Image from 'next/image';

<Image src="/hero.webp" alt="Spring collection"
       width={1600} height={900} priority />

Plain <img> tags in React components need loading="lazy" set by hand. Check the documentation for your Next.js version, as the image component's props have changed between releases.

Common lazy loading mistakes

How to verify the fix

Re-run PageSpeed Insights or Lighthouse and confirm that the defer offscreen images audit shows no remaining savings. Then open the DevTools Network panel filtered to "Img", reload without scrolling, and check that below-the-fold images only appear as you scroll toward them while the LCP image is still requested immediately. Watch the CLS value while scrolling to confirm the deferred images do not shift the layout. Lab results change right away, while field data in the Chrome UX Report reflects a trailing 28-day window.

Offscreen images differ between templates, so test more than the homepage. PerfDelta tests several pages on mobile and desktop and lists this Lighthouse opportunity with its estimated savings and the affected files, so you can see which templates still load images eagerly. Its lab and field data show whether LCP stayed stable after the change.

Frequently asked questions

What does "defer offscreen images" mean?

It means delaying images below the fold until the visitor scrolls near them, instead of loading them with the initial page. The usual way to do this is lazy loading with the loading="lazy" attribute.

How do I lazy-load images without a plugin?

Add loading="lazy" to the img and iframe tags that start outside the viewport and keep their width and height attributes. Modern browsers then defer those requests without any JavaScript.

Should I lazy-load every image?

No. Never lazy-load the hero image or the LCP element, because that delays the most important content and makes LCP worse. Load images above the fold eagerly and give the LCP image fetchpriority="high".

Does loading="lazy" work for CSS background images?

No. The attribute only applies to img and iframe elements. Background images need an IntersectionObserver that adds the background with a class once the element approaches the viewport.

Does WordPress lazy-load images automatically?

Yes. WordPress core adds loading="lazy" to content images and iframes and skips the first content image. Check that your theme, page builder or plugins do not lazy-load the hero image or add a second lazy-loading layer.

Can lazy loading make my page slower?

Yes, if it is applied too broadly. Deferring the LCP image slows LCP, and deferring images at the fold causes visible pop-in. Applied only to images that clearly start offscreen, it speeds up the initial load.

Thresholds and sources

Sources:

All sources retrieved on October 9, 2026. Thresholds and documentation change; the linked page is authoritative.

Test your site

Run a free scan to see whether this applies to your pages, on mobile and desktop, with lab and field data side by side.

Run a free test

A free account saves your tests and shows how your values change from one test to the next. Daily monitoring with field data and alerts starts with the Solo plan.

Related audits