On image-heavy sites (portfolios, agency showcases, photography, e-commerce lookbooks), lazy loading looks like free performance. Add loading=”lazy” everywhere, ship it, move on. Then PageSpeed Insights comes back with a red Largest Contentful Paint and a warning you did not expect: “Largest Contentful Paint image was lazily loaded”.
That is the paradox of lazy loading images for SEO. Done selectively, it is one of the cheapest performance wins available. Done to every image on the page, it actively delays the single visual that Google measures for speed. This guide explains the difference, with the exact markup, the exclusion rules, and the verification steps we use at Pixelbright on design-driven builds.
What is lazy loading, in plain terms?
Lazy loading is a strategy that tells the browser: this resource is not critical right now, fetch it only when the user is about to need it. For images, that means an image sitting 3,000 pixels down the page is not downloaded during the initial page load. It is downloaded when the user scrolls close to it.
The benefits on an image-heavy layout are real:
- Fewer bytes on initial load, which matters enormously on mobile data
- Less bandwidth contention, so the images that are visible get downloaded faster
- Lower hosting and CDN costs on galleries where most visitors never scroll to the bottom
- Better Core Web Vitals, as long as you exclude the right images
Historically this required JavaScript libraries and an IntersectionObserver. Today, browsers do it natively with a single HTML attribute, and that is what you should be using in 2026 unless you have a very specific reason not to.

Native lazy loading: the loading attribute
The implementation is one attribute on the <img> tag:
<img src="/images/project-detail.webp"
alt="Interior shot of the Maison Verre lobby"
width="1200"
height="800"
loading="lazy"
decoding="async">
It works the same way on iframes:
<iframe src="https://player.vimeo.com/video/123456" loading="lazy" title="Showreel"></iframe>
Native lazy loading is supported in every current major browser (Chrome, Edge, Firefox, Safari). Browsers that do not understand the attribute simply ignore it and load the image normally, so there is no fallback to write and no broken experience to worry about.
The three values you need to know
| Value | Behaviour | When to use it |
|---|---|---|
lazy |
Defers the download until the image approaches the viewport | Everything below the fold: grids, galleries, footers, related posts |
eager |
Loads immediately, regardless of position | Hero images, logos, anything visible on first paint |
| attribute omitted | Browser default, equivalent to eager loading | Acceptable for above-the-fold images, but being explicit is safer in a CMS |

The LCP trap: the images you must never lazy load
Largest Contentful Paint measures when the largest visible element in the viewport finishes rendering. On a design site, that element is almost always an image: the full-bleed hero, the first project thumbnail, the product shot. LCP is part of Core Web Vitals, and Core Web Vitals feed into Google’s page experience signals.
When you lazy load that element, you force the browser into a slower sequence:
- HTML is parsed, but the image is skipped by the preload scanner
- CSS and layout have to be computed
- Only then does the browser work out the image is in the viewport
- The request finally starts
You routinely add several hundred milliseconds to a full second of delay to your most important pixel. Google’s own guidance is blunt about this: lazy loading in-view content is detrimental and should be avoided.
How to decide what to exclude
Use these rules in order:
- Exclude anything inside the first viewport on mobile. Mobile is narrower and taller, so items that look below the fold on your 27-inch monitor may be fully visible on a phone. Test at 390 x 844 px.
- Exclude the LCP element, always. Identify it in DevTools (see below) rather than guessing.
- Exclude the first one or two items of a grid. On a portfolio grid, the top row is usually partially visible immediately.
- Exclude logos, above-the-fold icons and background-style images that form part of the first impression.
- Lazy load everything else. From the second screen down, defer it all.
Give your hero image a priority boost
Removing loading="lazy" from the hero is step one. Step two is telling the browser the image matters more than everything else competing for bandwidth:
<img src="/images/hero-2400.webp"
srcset="/images/hero-800.webp 800w, /images/hero-1600.webp 1600w, /images/hero-2400.webp 2400w"
sizes="100vw"
alt="Pixelbright studio hero visual"
width="2400"
height="1350"
loading="eager"
fetchpriority="high">
Key points on that snippet:
fetchpriority="high"moves the hero to the front of the request queue. Use it on one image per page only, otherwise the signal loses all meaning.widthandheightare mandatory on every image, lazy or not. They reserve layout space and protect your Cumulative Layout Shift score.- Never combine
fetchpriority="high"withloading="lazy"on the same element. It is a contradiction, and the lazy behaviour wins. - Do not add
decoding="async"to the LCP image; keep it for the deferred ones.
Is lazy loading bad for SEO? The honest answer
Native lazy loading on below-the-fold images is recommended by Google and is good for SEO. The risks come from how it is implemented, not from the concept. Here is where real damage occurs:
1. JavaScript-only lazy loading that hides the real URL
Old libraries put a placeholder in src and the real file in data-src. If the script fails, or if Googlebot does not trigger the scroll event, the image is never discovered and never indexed in Google Images. Native loading="lazy" keeps the real URL in src, so this problem disappears entirely. That is the single strongest reason to migrate off legacy scripts.
2. Content that only appears on scroll or on click
Googlebot does not scroll like a human; it renders with a very tall viewport. Lazy loading images is fine. Lazy loading text, links or whole sections behind a scroll event is risky. Keep crawlable content in the HTML.
3. Infinite scroll galleries with no paginated fallback
If your gallery loads more work as the user scrolls, make sure each batch is also reachable through real paginated URLs (/work/page/2/) with standard <a href> links. Otherwise most of your portfolio never enters the index.
4. Over-lazy-loading, the designer’s mistake
The most common issue we see on agency and portfolio sites: a global find-and-replace adds loading="lazy" to every single <img> in the template, hero included. The site looks optimised, the audit says otherwise.

How to check your implementation in Chrome DevTools
Verification takes about five minutes. Do it before you ship, not after a ranking drop.
Step 1: confirm which element is your LCP
- Open the page in a Chrome incognito window (extensions distort measurements)
- Open DevTools, go to the Performance panel
- Set the device toolbar to a mobile size and throttling to Slow 4G plus 4x CPU slowdown
- Click the reload icon to record a page load
- In the timings track, hover the LCP marker. DevTools highlights the element and names it in the summary
Then inspect that element in the Elements panel. If you see loading="lazy" on it, you have found your problem.
Step 2: verify deferral actually happens
- Open the Network panel and filter by Img
- Reload without scrolling and count the requests. Only above-the-fold images should appear
- Scroll slowly and watch new image requests fire as sections come into view
- If every image loads at once, your attribute is missing, overwritten by a plugin, or cancelled by a JS library
Step 3: run Lighthouse locally
In the Lighthouse panel, run a mobile performance audit and look for these audits:
- “Largest Contentful Paint image was lazily loaded” (fails if the hero is deferred)
- “Defer offscreen images” (fails if you are not lazy loading enough)
- “Properly size images” and “Serve images in next-gen formats”
A healthy image-heavy page passes the first and the second at the same time. That balance is the whole point.

How to check in PageSpeed Insights
Lighthouse in DevTools uses your machine and your connection. PageSpeed Insights adds something more valuable: field data from real Chrome users (CrUX), when your page has enough traffic.
- Go to PageSpeed Insights and test the exact URL, not just the homepage
- Start with the Mobile tab. It is the one that reflects how most visitors experience the site
- Read “Discover what your real users are experiencing” first. Your LCP target is under 2.5 seconds at the 75th percentile
- Open the LCP element entry in the diagnostics to see exactly which image is being measured
- Expand the LCP breakdown: a large “Resource load delay” is the classic fingerprint of a lazy-loaded hero
- Re-test 24 to 48 hours after deploying, and keep an eye on the Core Web Vitals report in Search Console for field data over 28 days
Reading the LCP sub-parts
| Sub-part | What a high value usually means |
|---|---|
| Time to First Byte | Server, hosting or caching issue, not an image issue |
| Resource load delay | The hero is lazy loaded, injected by JavaScript, or set as a CSS background |
| Resource load duration | File is too heavy: compress, convert to WebP or AVIF, add srcset |
| Element render delay | Render-blocking CSS or fonts, or a reveal animation gating the image |
Platform notes for image-heavy sites
- WordPress: core adds
loading="lazy"automatically and tries to skip the first images in the content, plus it appliesfetchpriority="high"to the likely LCP image. It is not always right on custom themes and page builders. Check the rendered HTML of your hero manually. - Page builders and sliders: many output images as CSS backgrounds, which the browser discovers late. For a hero, prefer a real
<img>element, or add a<link rel="preload" as="image">in the head. - Webflow, Shopify, Squarespace: all offer a per-image loading setting. Set the hero to eager and leave the rest lazy.
- Masonry and filterable grids: JS layout scripts sometimes rewrite
srcvalues. Re-check the first row after the script runs. - Animation libraries: fade-in-on-scroll effects applied to the hero can delay render even when the file loads fast. Exclude the first viewport from scroll reveals.

Quick implementation checklist
- Identify the LCP element on mobile for each page template
- Set it to
loading="eager"withfetchpriority="high" - Add
loading="lazy"anddecoding="async"to every image below the first viewport - Add explicit
widthandheightto every image - Serve WebP or AVIF with a proper
srcsetandsizes - Write descriptive
alttext, since Google Images still relies on it - Keep real image URLs in
src, never only indata-src - List key images in an image sitemap if your gallery is large
- Verify in DevTools, then in PageSpeed Insights, then in Search Console field data
FAQ
Is lazy loading good or bad for SEO?
It is good for SEO when applied to below-the-fold images using the native loading attribute, because it improves load times without hiding content from crawlers. It becomes harmful when applied to in-view images, especially the LCP element, or when the real image URL is hidden behind JavaScript.
Does Google index lazy loaded images?
Yes, when you use native loading="lazy", because the actual file path stays in the src attribute and Googlebot renders with a very tall viewport. Images that only get a real URL after a user scroll event, through a custom script, may not be indexed.
Should I lazy load the hero image?
No. The hero is almost always the Largest Contentful Paint element. Lazy loading it delays discovery and directly worsens your LCP score.
Why is loading=”lazy” not working on my site?
The usual causes are: the image is already inside or near the viewport so the browser loads it immediately, a JavaScript library or optimisation plugin is overwriting the attribute, the image is a CSS background rather than an <img> element, or the page is short enough that every image is within the browser’s loading threshold.
Which is better, lazy loading or eager loading?
Neither is better overall; they serve different positions on the page. Eager loading is correct for everything visible on first paint, lazy loading is correct for everything else. A well-built image-heavy page uses both. This guide goes deeper on it.
Does lazy loading help Core Web Vitals?
It helps indirectly by freeing bandwidth for critical resources, which can improve LCP. It does not improve LCP by itself, and it hurts LCP if applied to the measured element. It also has no direct effect on Interaction to Next Paint, though reducing decode work on heavy pages can help responsiveness.
Can I lazy load videos and iframes too?
Yes. The loading="lazy" attribute works on <iframe> elements, which is particularly useful for embedded players and maps. For self-hosted video, use preload="none" with a lightweight poster image.
Need an audit on your image-heavy build?
If your portfolio or e-commerce site is visually strong but slow to paint, the fix is usually a handful of attributes in the right places rather than a rebuild. The Pixelbright team can audit your templates, pinpoint the LCP element on each one, and ship a loading strategy that keeps the design intact. The piece Lazy Loading Techniques For Images, Videos, And Iframes makes a good next read.