Image Lazy Loading: Fixing Missing or Misused Lazy Loading
Lazy loading tells the browser to wait before fetching an image until the visitor scrolls close to it. Used well, it saves data on long pages and leaves bandwidth for what people see first.
Used badly, it does harm in two opposite ways: pages that fetch every image at once, including dozens nobody scrolls to, and pages that make the main photo wait, so the top of the page looks empty for longer. This guide shows how to sort your images into those that should load eagerly and those that should wait, and how to apply the right attribute in each place.
How native lazy loading behaves
Adding loading="lazy" to an img or iframe changes when the
browser requests it. Instead of fetching it as soon as the HTML is read, the browser waits until layout
is done and the element is within a certain distance of the viewport. As the visitor scrolls, images
are requested a little before they come into view so they are usually ready in time.
A few details matter for getting it right:
- The distance is decided by the browser. Chromium-based browsers start fetching lazy images some way before they enter the viewport, and adjust the distance by connection speed. You cannot set it in HTML. That is why lazy loading is safe for images a screen or two down.
- Lazy images wait for layout. Even an image that turns out to be on screen is requested later than an eager one, because the browser must first work out where it sits. On the main image, that delay is exactly what you do not want.
- The default is eager. Leaving the attribute off is the same as
loading="eager". You only need to writeeagerexplicitly when overriding a platform or plugin that addslazyeverywhere. - It works on images and iframes only. CSS backgrounds, video posters and images inserted by scripts are not affected.
- It needs dimensions to work well. An image with no
widthandheighttakes up no space until it loads, which can pull many images into the loading zone at once and makes the page shift when they arrive.
All current major browsers support the attribute for images. Older browsers simply ignore it and load images normally, so it is safe to use without a fallback.
The two failures the audit reports
| Finding | What it looks like | Effect |
|---|---|---|
| Off-screen images loaded eagerly | A long article or product grid requests every image as the page opens. | Wasted data for visitors who never scroll, and bandwidth taken away from the first screen. |
| Main image lazy-loaded | The hero or first product photo has loading="lazy",
or a data-src that a script fills in. | The image the visitor sees first starts downloading late, which slows Largest Contentful Paint. |
| Script-based lazy loading | Images start as a placeholder or blank GIF and a JavaScript library swaps in the real address. | Nothing loads until the script runs, and the real address is hidden from the browser's preload scanner. |
The first is a waste problem. The second and third are speed problems for the part of the page that matters most. A single page can have all three, typically when a theme lazy-loads everything with a script and a page builder then adds eager images further down.
The fix, step by step
Step 1
Record what loads before any scrolling
Open the page in a private window with developer tools, disable the cache, filter the Network panel to images and reload without touching the scroll bar. Every image listed was requested at page load. Repeat with device emulation at a phone width, because far more of the page is off-screen on a phone. Compare the list with what is actually visible on the first screen, and note the file names that load even though they sit well below it.
Step 2
Sort images into first-screen and later
For each template, decide which images are visible without scrolling on common phone and desktop sizes. Usually that is the logo, the hero or first product photo, and perhaps the first row of a grid. Everything else is later. Where an image is on screen for desktop but not phones, treat it as first-screen; loading one image eagerly costs little, while lazy-loading a visible image delays it. Find the Largest Contentful Paint element with PageSpeed Insights as well.
Step 3
Take lazy loading off first-screen images
Remove loading="lazy" from every first-screen image, or set loading="eager"
where a platform adds lazy by default. On the single most important image, usually the Largest Contentful
Paint element, add fetchpriority="high" so the browser requests it ahead of other files.
Do not add high priority to several images, because priorities only help when they single something out.
Make sure that main image is a normal img in the HTML, not injected later by a script.
Step 4
Add loading="lazy" to everything further down
In the templates and components that output images below the first screen, add loading="lazy"
and decoding="async", and make sure width and height are present
so the space is reserved. Good candidates are article body images after the first, product grids beyond
the first row, related items, review photos, footer logos and embedded maps or videos in iframes. In
a loop, a simple rule works well: the first two or three items eager, the rest lazy.
Step 5
Replace script-based lazy loading with the native attribute
If your images use data-src or data-srcset
with a class such as lazyload, move the real addresses back into src and srcset,
add loading="lazy" where appropriate, and remove the library and its placeholder images.
Search templates for data-src to find every instance. Keep any matching noscript
fallback only until the change is live, then delete it, because it is no longer needed.
Step 6
Handle carousels, tabs and hidden images
Images inside sliders, tabs, accordions and menus are often off-screen or hidden even though
they sit near the top of the HTML. Keep the first visible slide eager and give the others loading="lazy";
the browser will fetch them as they come near the viewport. Images inside an element set to display: none
are not fetched while lazy, which is what you want for a closed accordion, but check they appear promptly
once opened.
Step 7
Confirm images still reach search engines
Run the URL Inspection tool in Search Console on a changed page, view the tested page's
HTML and screenshot, and confirm the real image addresses are present in src or srcset.
Googlebot renders pages with a tall viewport rather than scrolling, so native lazy images are found,
but images that only appear after scroll events or clicks may not be. Then run the Image Optimisation
audit and PageSpeed Insights again to compare.
Before and after markup
The examples are from a fictional outdoor clothing shop, Fellside Outfitters.
Top of a category page
<!-- Before: theme lazy-loads the banner with a script -->
<img class="lazyload" src="/img/blank.gif"
data-src="/img/autumn-banner-1600.webp" alt="">
<!-- After: real address in the HTML, requested first -->
<img src="/img/autumn-banner-1600.webp"
srcset="/img/autumn-banner-800.webp 800w,
/img/autumn-banner-1600.webp 1600w"
sizes="100vw" width="1600" height="600"
fetchpriority="high" alt="">
A product grid loop
{% for product in products %}
<img src="{{ product.image_640 }}"
width="640" height="800"
alt="{{ product.image_alt }}"
{% if loop.index > 4 %}loading="lazy" decoding="async"{% endif %}>
{% endfor %}
The first four products, which fill the first row on desktop and the first screen on most phones, load immediately. The rest wait. Adjust the number to match your grid.
An embedded map and video
<iframe src="https://www.example.com/embed/map?store=keswick"
width="600" height="400" loading="lazy"
title="Map showing the Keswick shop"></iframe>
Third-party embeds are among the heaviest things on a page because they load their own scripts and styles. Lazy-loading the iframe keeps all of that off the initial load when the embed sits below the first screen. For video embeds, a lighter approach is a thumbnail image with a play button that loads the real player only when clicked.
Platform notes
WordPress
WordPress core adds loading="lazy" to images automatically and, in recent versions, tries
to leave the first content images eager and to mark the likely main image with fetchpriority="high".
Its guess depends on the theme outputting images through core functions. Problems arise when an optimisation
plugin also lazy-loads with a script, producing two systems at once, or when a page builder hero is not
recognised. Turn off one of the two lazy-load systems, and exclude the hero by class in the plugin's
settings.
Shopify
The image_tag Liquid filter accepts a loading parameter, and many current
themes set it to lazy for all but the first section. Older themes often ship lazysizes; check the theme's
JavaScript assets and data-src attributes before assuming native loading is in place.
Next.js
The Next.js Image component lazy-loads by default. The main image needs to be marked as a priority, as described in our Next.js images guide.
Page builders and sliders
Many slider plugins and page builders have their own lazy-load switches that act independently of the platform's. Look for them in the widget or slider settings when an image stays lazy after you have changed the theme.
Edge cases
- CSS background images. They cannot use
loading="lazy". For a heavy background far down the page, add the class that applies it with an IntersectionObserver script, or convert it to animgwithobject-fit: coverso native lazy loading applies. - Images added by JavaScript. Infinite scroll and "load more" buttons already load
on demand. Give the images they add
loading="lazy"anyway, and make sure paginated URLs with plain links exist so crawlers can reach the same products. - Print. Lazy images not yet loaded may be missing when someone prints a long page. Browsers are improving here, but for pages people print, such as recipes or instructions, test it.
- Anchor links. Jumping to a section half-way down loads the images there immediately, which is fine; but if they have no dimensions, the page can shift and the anchor target moves. Dimensions solve it.
- Very short pages. On a page where everything fits in one or two screens, lazy loading saves almost nothing. It is still harmless below the first screen, but do not spend effort on it.
- Emails and feeds. Image tags copied into newsletters or RSS feeds may keep a lazy
attribute or a
data-src. Email clients ignore the first and break on the second, so strip script-based markup from feed output.
loading="lazy" to every image by a blanket rule in a template
is the most common way the main image ends up lazy. Put the rule in the loop or component with an explicit
exception for first-screen positions.Keeping lazy loading correct
- Make position an input. Give image components a parameter such as "above the fold" or "priority" that switches between eager with high priority and lazy, so each template decides deliberately.
- One system only. Use native lazy loading and remove script libraries. Document which plugin or setting controls it.
- Check after theme and plugin updates. Updates often reset or add lazy-load options. Re-run the Image Optimisation audit on the home page and a key product or article page afterwards.
- Watch field data. Keep an eye on Largest Contentful Paint in Search Console's Core Web Vitals report. A sudden rise after a release is often a main image that has become lazy.
1. Should I lazy-load all the images on my page?
No. Lazy-load images that start below the first screen, and load everything visible on arrival normally. Lazy-loading a visible image makes the browser wait for layout before requesting it, which slows the moment the page looks complete. The main image at the top, usually the Largest Contentful Paint element, should never be lazy and benefits from fetchpriority set to high. A simple rule in loops, such as eager for the first few items and lazy for the rest, works well.
2. Does lazy loading hurt SEO?
Native lazy loading with the loading attribute does not, because the real image address is in the HTML and Googlebot renders pages with a tall viewport so lazy images are fetched. Problems come from script-based methods that keep the real address in a data attribute and only swap it in after a scroll event or click, which a crawler may never trigger. Check a changed page with Search Console's URL Inspection tool and confirm the images appear in the rendered HTML.
3. What is the difference between loading lazy and fetchpriority?
They control different things. The loading attribute decides whether the browser waits until an image is near the viewport before requesting it. Fetchpriority changes how important a request is relative to others once it is made. Use loading lazy for images further down the page, and fetchpriority high on the single most important image at the top. Using both on the same image is contradictory, because a lazy image still waits for layout however high its priority.
4. Why are my lazy-loaded images loading straight away?
Browsers start fetching lazy images some distance before they reach the viewport, so on a short page most of them may load immediately, which is expected. Other causes are images with no width and height, which collapse to nothing and all fall within range, a hidden carousel whose slides sit side by side in the layout, or a script that forces loading. Add dimensions and test on a long page in a phone-sized window.
5. Should I remove lazysizes or other lazy-load libraries?
In most cases, yes. They were needed before browsers supported the loading attribute, but now they add a script download, hide real image addresses from the browser's early scanner and delay images until the script runs. Move the addresses back into src and srcset, add the native attribute where appropriate and remove the library. Keep a script only for special cases native loading cannot cover, such as heavy CSS backgrounds far down a page.
6. Can I lazy-load iframes and videos too?
Iframes support the same loading attribute, and it is one of the most effective places to use it, because embedded maps, videos and social posts pull in their own scripts and styles. The video element does not support it, but setting preload to none or metadata on videos below the first screen has a similar effect. For YouTube and similar embeds, a thumbnail that loads the player only on click saves even more.
7. Does lazy loading affect Core Web Vitals?
It can help or hurt. Correctly used, it frees bandwidth for the first screen, which can help Largest Contentful Paint on image-heavy pages. Applied to the main image, it delays that image and makes Largest Contentful Paint worse. Lazy images without width and height can also cause layout shift when they appear. It has little direct effect on Interaction to Next Paint, though removing a heavy lazy-load script reduces main-thread work.
The Image Optimisation audit checks every image on your page for file size, format, dimensions, alt text, lazy-loading, layout shift and CDN delivery, and ranks its recommendations by their effect on loading and bandwidth.
Open Image Optimisation Fixes โ