Lazy loading images is genuinely good advice that gets applied genuinely badly. The idea: don’t load images the visitor can’t see yet. The common implementation: don’t load images the visitor is currently looking at either. That’s how a performance feature turns a site sluggish, and why your PageSpeed score dropped after you "optimized."
Lazy loading below the fold is optimization. Lazy loading the hero image is telling the browser to procrastinate on the one thing that matters.

The hero image mistake
Largest Contentful Paint, the Core Web Vitals metric Google weighs heaviest, measures when your biggest visible element finishes rendering. On most sites that’s the hero image. Lazy loading works by telling the browser "this image isn’t urgent, fetch it when you get around to it." Apply that to the hero and you’ve deprioritized the exact image LCP is timing.
The browser dutifully waits, your LCP balloons past 2.5 seconds, and the page feels slower because the main thing visibly pops in late. Google’s own research on lazy-loaded LCP images found this pattern consistently degrading performance across the web.
The reason is mechanical. Normally the browser spots image tags while it is still reading the HTML and starts downloading them straight away. A lazy image can’t start until the browser has worked out the layout and confirmed the image is near the screen. On a phone on a slow connection, that wait is long enough to see.
How it happens without you choosing it
Three common paths. WordPress adds loading="lazy" to images by default: core tries to skip the first images, but themes and builders that render heroes in unusual ways defeat the detection. Optimization plugins often apply JavaScript-based lazy loading to everything, hero included, and JS-based lazy loading is worse than the native kind: the image can’t even start until the script loads and runs. And sliders are the worst case: many load slide one lazily and inject the rest with JavaScript, making your most important visual doubly delayed.
Ready-made themes are especially prone to this, because they build heroes from generic blocks the lazy loading logic can’t recognise. It is one of the quieter arguments for a custom website over a template: every template decides what loads first, and a custom build puts that decision in your hands. Shop themes and apps do the same thing, which is part of why Shopify stores get slow too.
How to check if your hero is lazy loaded
You don’t need to guess. Two checks take a couple of minutes:
- Run the page through PageSpeed Insights and find the LCP element in the diagnostics. If it is an image, Lighthouse will flag it when that image was lazily loaded.
- Right-click the hero, choose Inspect, and read the tag. Look for
loading="lazy", or for adata-srcattribute and a class such aslazyload. Those last two mean a script is in charge, and the real image address isn’t insrcat all.
Check on a mobile view as well as desktop. What sits above the fold changes with screen size, and the image that matters on a phone is not always the one that matters on a laptop.

The fix: priority, not just exclusion
The hero image should be the opposite of lazy. Three steps:
1. Remove lazy loading from above-the-fold images
The hero, the logo, anything visible on arrival. In most optimization plugins that’s an exclusion field; in a proper theme it’s handled per-template.
2. Actively prioritize the hero
Add fetchpriority="high" to the hero image and preload it in the head. Now the browser fetches it first instead of merely not-last.
In practice the hero tag ends up looking like <img src="hero.webp" width="1600" height="900" fetchpriority="high" alt="...">, with no loading attribute at all. If the image uses srcset, make sure the preload describes the same set, or the browser may download one size for the preload and a different one for the page.
3. Keep lazy loading everywhere else
Gallery grids, footer content, long-page images. That’s where the technique belongs and where it genuinely helps.
Where lazy loading images actually pays off
Used on the right things, lazy loading saves real bandwidth and gets the first screen out faster, because the browser isn’t fetching thirty gallery photos nobody has scrolled to yet. Good candidates:
- Image grids on portfolio, gallery and product listing pages.
- Images deep inside long blog posts.
- Embedded YouTube videos and maps, which accept
loading="lazy"on the iframe. - Footer logos, badges and partner strips.
Prefer the native attribute over a JavaScript library. Every current browser supports it, it needs no extra script, and it fails safe: if anything goes wrong, the image simply loads.
Mistakes that undo the fix
- Running native lazy loading and a plugin’s script version at the same time.
- Lazy images with no width and height, which reserve no space and shift the layout when they arrive.
- Preloading the hero but leaving it at a heavy size. Priority helps, but a lighter file helps more, so compress it as WebP or AVIF as well.
Verify the result
Re-run PageSpeed Insights and check the LCP element it reports. If LCP was 4+ seconds with a lazy-loaded hero, this fix alone commonly claws back one to two full seconds.
If the score’s still red afterward, the problem has more layers (image weight, render-blocking code, server response), which is the point where a full speed optimization pass earns its fee: it fixes the causes in the right order instead of stacking plugins on symptoms.




