/ โ† Image Optimisation Fixes / Lazy loading not working
In this guide
  1. What loading lazy actually does
  2. Why lazy loading seems not to work
  3. Correct markup for images and iframes
  4. Platform notes
  5. A worked example
  6. The fix, step by step
  7. Edge cases worth knowing
  8. Keeping it working
  9. Questions and answers

What loading lazy actually does

The loading attribute on an img or iframe element tells the browser whether it may delay fetching that resource. With loading="lazy", the browser postpones the request until the element is within a certain distance of the viewport. With loading="eager", or no attribute at all, it fetches the resource straight away.

The important detail is that distance. Browsers do not wait until an image is on screen, because it would then appear blank as you scroll. They start the request well before, and further ahead on slower connections, so the file has time to arrive. Chrome uses a threshold measured in thousands of pixels on slow connections and somewhat less on fast ones. On a short page, that can mean every lazy image loads during the initial page load, which is correct behaviour rather than a failure.

What you seeUsually means
Images just below the fold load immediatelyNormal: they are within the browser's loading distance
Every image on a long page loads at onceA script or attribute is overriding lazy loading
Images stay blank or never loadA lazy-load script failed, or src is empty
Images load twiceNative and script lazy loading are both active
Background images ignore the attributeExpected: the attribute does not apply to CSS
Main image loads late and LCP is poorThe hero is lazy-loaded when it should not be

All current major browsers support lazy loading on images, and on iframes in recent versions. Browsers that do not support it simply ignore the attribute and load the resource normally, so it is safe to use. Chrome also does not lazy-load when JavaScript is turned off, as a privacy measure.

Why lazy loading seems not to work

The attribute is added too late

Some themes and plugins add loading="lazy" with JavaScript after the page has loaded. By then the browser has already read the HTML and started downloading every image. Check the original HTML with view source, not the Elements panel, which shows the page after scripts have changed it. When creating images in JavaScript yourself, set loading before src, because the request can start as soon as src is assigned.

An old lazy-load script is also running

Before native lazy loading, scripts such as lazysizes kept the real address in data-src and left src empty or set to a tiny placeholder. Mixing these with the native attribute causes trouble. A script that copies every data-src into src on page load defeats lazy loading entirely, while a script that fails to run leaves images blank. Pick one method, and for most sites that should be native lazy loading.

<!-- Script-based: the browser sees no real image until the script runs -->
<img src="placeholder.gif" data-src="photo.webp" alt="Garden patio">

<!-- Native: the browser sees the real address and decides when to fetch it -->
<img src="photo.webp" width="800" height="600" alt="Garden patio" loading="lazy">

CSS background images

The attribute applies only to img and iframe elements. A background set with background-image: url() loads when the browser applies the rule to an element in the layout, whatever attributes the element carries. To lazy-load a decorative background, add the rule with a class once the element approaches the viewport, using an IntersectionObserver, or change the image to an img element where that suits the design.

Hidden and carousel images

Images inside hidden tabs, accordions, menus and carousels behave differently depending on the browser and on how they are hidden. Some load when the page loads, some only when shown. If these matter to you, test each component in the browsers your visitors use rather than assuming. For carousels, keep the first slide eager and the rest lazy.

For a wider view of image loading, see fixing image lazy loading and image speed fixes.

Correct markup for images and iframes

A lazy image below the fold needs a real src, its dimensions and meaningful alt text:

<img src="/img/kitchen-after.webp"
     srcset="/img/kitchen-after-600.webp 600w, /img/kitchen-after-1200.webp 1200w"
     sizes="(max-width: 700px) 100vw, 700px"
     width="1200" height="800"
     alt="Kitchen after the refit, with oak worktops"
     loading="lazy" decoding="async">

The width and height let the browser reserve the right space before the file arrives, so the content below does not jump when it loads. That matters more for lazy images than any others, because they often arrive while the visitor is reading. See image dimensions and image layout shift.

A video embed or map works the same way. Lazy loading an iframe can save a great deal, because a single video player can pull in hundreds of kilobytes of scripts:

<iframe src="https://www.youtube-nocookie.com/embed/VIDEO_ID"
        width="560" height="315"
        title="Product demo video"
        loading="lazy"
        allow="accelerometer; encrypted-media; picture-in-picture"
        allowfullscreen></iframe>

Never lazy-load the first screen

Anything visible when the page first loads should load eagerly. The main image in particular should have no loading attribute and should carry fetchpriority="high" instead:

<img src="/img/hero-1200.webp" width="1200" height="600"
     alt="Fitted wardrobes in a bright bedroom" fetchpriority="high">

A lazy hero image is one of the most common causes of a slow Largest Contentful Paint. The mobile LCP guide covers this in detail.

Platform notes

A worked example

A travel blog adds loading="lazy" to all 40 images in a long guide, but the Network panel shows all 40 downloading as the page opens, around 6 MB in total. View source shows each image as <img src="blank.gif" data-src="..." loading="lazy">. The theme also bundles an old lazy-load script whose fallback mode copies every data-src into src as soon as the page has loaded.

The owner turns off the theme's lazy-load option, which removes the script and writes the real addresses back into src. Native lazy loading is now the only method. On reload, the Network panel shows the first five images loading, with the rest appearing as the page scrolls. The initial page weight falls to about 900 KB, and the hero image, which was also lazy, is changed to load eagerly with high priority.

The fix, step by step

Step 1

Check the original HTML

Open the page, then open view source with view-source: before the address, or press the shortcut for it in your browser. Search for loading= and check that the images and iframes below the fold carry loading="lazy" in the HTML itself. Do not rely on the Elements panel, which shows the page after scripts have changed it. If the attribute is missing from the source but appears in the Elements panel, it is being added too late and your theme or plugin needs changing.

Step 2

Test with the Network panel

Open Chrome DevTools, choose the Network panel, filter by Img and tick Disable cache. Reload the page without scrolling and count how many images loaded. Then scroll slowly to the bottom and watch new images appear in the list as you approach them. If images further down only load as you scroll, lazy loading works. If every image loads at once on a long page, something is overriding it, so move on to the next steps.

Step 3

Look for an old lazy-load script

In the page source, search for data-src, data-srcset and class names such as lazyload or lazy. Also check the list of scripts for names such as lazysizes or a theme lazy-load file. If you find them, a script-based method is running alongside or instead of the native attribute. Turn off the script in the theme or plugin settings so real addresses go back into src, then repeat the Network panel test to confirm the change.

Step 4

Use one lazy-loading method

Choose native lazy loading as the single method and turn off every other lazy-load option: in the theme, the page builder, the image optimisation plugin and the caching plugin. On WordPress, core already adds the attribute, so a plugin is optional. Clear the page cache and any CDN cache after changing settings. Confirm in view source that images now have a real src and loading="lazy", and in the Network panel that nothing loads twice.

Step 5

Make the first screen eager

Find every image and iframe visible on first load, including the logo and the hero. Remove loading="lazy" from them, and add fetchpriority="high" to the main image only. Most plugins have an option to exclude the first images or images with a certain class. Run PageSpeed Insights and confirm the warning about a lazily loaded LCP image has gone, and check in the Network panel that the hero is one of the first images requested.

Step 6

Add width and height

Give every lazy image a width and height that match its real proportions, or set aspect-ratio in CSS. Without them, the browser does not know how much space to keep, and the page jumps when each lazy image arrives. The values set the shape, not the displayed size, so responsive CSS still works. Reload and scroll the page, and confirm that text does not move as images appear. Check the layout shift result in PageSpeed Insights too.

Step 7

Handle background images separately

List any large images set with background-image in CSS, since the attribute cannot affect them. For decorative backgrounds below the fold, apply the background with a class added by an IntersectionObserver when the element comes near the viewport. For backgrounds that carry meaning, change them to img elements with loading="lazy" and suitable alt text. Confirm in the Network panel that those files now load only as you scroll towards them.

Step 8

Retest across browsers and templates

Repeat the Network panel scroll test on your longest page, a product or service page and the home page, because templates load images differently. Check the same pages on a phone and in Safari or Firefox if your visitors use them. Expect images within a screen or two of the viewport to load straight away. Finally, run PageSpeed Insights on mobile to confirm total page weight has fallen and LCP has not got worse.

Edge cases worth knowing

Keeping it working

Keep one lazy-loading method on the site and write down which one it is, so a new plugin or theme does not quietly add a second. After installing an optimisation plugin or changing theme, view the source of a long page and check the first image is eager and the rest carry the native attribute with real src values.

Every few months, run PageSpeed Insights and look for the audit warning that the LCP image was lazily loaded, and repeat the Network panel scroll test on your longest page. For a step-by-step testing method, see how to check lazy loading is working.

Questions and answers

1. Why are my lazy images loading straight away?

Lazy images often load straight away because they are within the distance the browser uses to start fetching them before they scroll into view. Browsers deliberately load lazy images early so they are ready when the visitor reaches them, and the distance is larger on slow connections. On a short page that can include every image. If every image on a long page loads at once, look for a lazy-load script copying data-src into src, or for the attribute being added by JavaScript.

2. Does loading lazy work on background images?

No, the loading attribute only works on img and iframe elements, so it has no effect on images set as CSS backgrounds. A background image loads when the browser applies the CSS rule to an element in the layout. To delay a background below the fold, add the class that sets it with an IntersectionObserver once the element approaches the viewport, or change the image to an img element with the lazy attribute if it suits the design.

3. Should I use a lazy-load plugin or native lazy loading?

For most sites native lazy loading is the better choice, because it is built into all current major browsers, needs no script and keeps real image addresses visible to search engines. Script-based plugins were needed before browsers supported the attribute, and they can still help with special cases such as background images. Do not run both at once, as that can cause images to load twice or not at all. On WordPress, core adds native lazy loading automatically.

4. Can lazy loading hurt my LCP?

Yes, lazy loading hurts LCP when it is applied to the main image or anything else visible on first load. The browser delays fetching lazy images until it knows where they sit on the page, so a lazy hero starts downloading later than it should. PageSpeed Insights warns about this. Remove the lazy attribute from images on the first screen and add fetchpriority high to the main image, while keeping lazy loading for images further down.

5. Does Google index lazy-loaded images?

Google can index images that use native lazy loading, because its crawler renders the page with a tall viewport and fetches images found in the HTML. Problems arise with script-based lazy loading that keeps the real address only in a data attribute and waits for scroll events, since the crawler does not scroll like a person. Use the native loading attribute with the real address in src, and check rendered pages with the URL Inspection tool in Google Search Console.

6. Does loading lazy work on iframes?

Yes, loading lazy works on iframes in all current major browsers, although support arrived later than for images, so very old browser versions load iframes normally. It is especially useful for video embeds and maps, which can pull in large amounts of script. Add the attribute along with a width, height and descriptive title. As with images, do not lazy-load an iframe that is visible when the page first loads, or it will appear later than it should.

7. Why do some lazy images never appear?

Lazy images that never appear are usually caused by a lazy-load script that failed to run, leaving src empty or pointing at a placeholder while the real address sits in data-src. A JavaScript error elsewhere on the page, a script deferred too far or a blocked file can all stop it. Check the Console for errors and the page source for data-src attributes. Switching to native lazy loading with the real address in src removes this risk entirely.

Check how your images load

The image optimisation checker on aiwebpageseo.com flags lazy-loaded hero images, missing dimensions and oversized files on any page, so you can see which images need changing.

Open Image Optimisation Fixes โ†’
๐Ÿ’ฌ Got a problem?
Related Guides: All Image Optimisation Fixes  ·  How to check lazy loading is working  ·  Fixing image lazy loading  ·  Why is my LCP so high on mobile  ·  Fixing image dimensions  ·  Learning Hub

About aiwebpageseo

aiwebpageseo.com is a data-driven SEO and AEO (Answer Engine Optimisation) platform providing a free suite of technical SEO tools. Rather than relying on AI-theorised assumptions, the platform analyses live URL performance, delivering objective diagnostics, page speed metrics, CLS debugging, and site crawl data alongside actionable technical tutorials.