RevealTheme logo
Back to Blog

Lazy Loading Images: What Plugins Get Wrong

Lazy Loading Images: What Plugins Get Wrong
The RevealTheme Team

By

·

Lazy loading is supposed to make pages faster. You defer off-screen images until the user scrolls toward them, the browser downloads less upfront, and everyone wins. That is the theory. In practice, a large share of WordPress lazy-loading setups actively hurt performance, and the culprit is almost always a plugin doing too much, doing it the old way, or doing it to the wrong images. Here is exactly where they go wrong and how to fix each problem.

Mistake 1: Lazy-loading the LCP image

This is the single most damaging thing a lazy-loading plugin can do, and it is shockingly common. Your hero image, the first product photo, the featured image at the top of a post: these are usually your Largest Contentful Paint element. When a plugin slaps loading="lazy" on that image, the browser deprioritizes it, often waiting until layout is computed before it even begins the download. The result is a measurably slower LCP, the exact opposite of what you wanted.

You want LCP under 2.5 seconds. Lazy-loading the above-the-fold image routinely pushes it past that. The correct treatment for the hero image is the opposite of lazy: it should be eagerly loaded and marked fetchpriority="high" so the browser fetches it first.

WordPress core actually gets this right out of the box. Since WordPress 5.9, core skips lazy loading on the first content image, and since WordPress 6.3, core automatically adds fetchpriority="high" to the image it identifies as the likely LCP element. The problem is that aggressive plugins override this. If your plugin "lazy loads all images" with no above-the-fold exclusion, it is undoing core's careful work. Look for a setting like "exclude first N images" or "DOM-based above-the-fold exclusion" (Perfmatters and WP Rocket both offer this) and make sure your hero is excluded.

Mistake 2: Injecting JavaScript when the browser already does this natively

Native browser lazy loading via the loading="lazy" attribute has been supported across all current browsers for years, and WordPress core has emitted it automatically since WordPress 5.5 (August 2020). It costs zero JavaScript and zero render-blocking overhead.

Yet plenty of plugins still ship a lazysizes-style JavaScript library that replaces src with data-src, waits for scroll events, and swaps the real source in at runtime. In 2026 this is mostly redundant weight. It adds a script to parse and execute, it depends on JavaScript succeeding, and it can leave broken images for crawlers and users on flaky connections. Older plugins like a3 Lazy Load were built around this JS-first model.

The fix: prefer native lazy loading. If a plugin offers a choice, pick the native attribute over the JS engine. The legitimate exceptions are background images set via CSS and video/iframe embeds, which native loading="lazy" does not fully cover and where a small script genuinely adds value.

Mistake 3: No reserved dimensions, so lazy loading creates layout shift

When an image loads late and you have not told the browser how tall it will be, surrounding content jumps as the image pops in. That is Cumulative Layout Shift, and you want it under 0.1. Lazy loading makes CLS worse precisely because the image arrives after the user is already reading.

The fix is to always emit explicit width and height attributes (or a CSS aspect-ratio) so the browser reserves the exact space before the file downloads. WordPress core adds width and height to content images automatically, but page builders and some galleries strip or omit them, especially for background and CSS-driven images. If your lazy-loaded images cause shift, the lazy loading is not the disease, the missing dimensions are. Audit the actual rendered HTML rather than trusting the plugin's checkbox.

Mistake 4: Lazy-loading everything indiscriminately

"Lazy load all images" sounds thorough. It is actually crude. A good lazy-loading strategy excludes:

  • Tiny images: icons, avatars, and sub-100px graphics cost more in deferral overhead than they save in bytes.
  • The logo and anything in the header: these are above the fold by definition.
  • Images already inside the initial viewport: deferring something the user can already see is pure loss.
  • SVGs and inline data URIs: there is nothing to defer.

Plugins that expose only an on/off switch with no exclusion rules push you toward the wrong default. Tools like WP Rocket, Perfmatters, and LiteSpeed Cache let you exclude images by class, by URL pattern, or by count from the top of the page. Use those rules. A blanket "lazy everything" toggle is a sign the plugin was built for marketing screenshots, not Core Web Vitals.

Mistake 5: Double lazy loading

Because core now emits loading="lazy" on its own, a plugin that also processes the same images can stack two mechanisms on top of each other: the native attribute plus a JavaScript swap. Symptoms include images that never load until you scroll twice, placeholder flicker, broken images on the first paint, and conflicts where the plugin's data-src rewrite fights core's attribute.

If you run a dedicated lazy-loading plugin or a caching plugin's lazy-load module, you usually want exactly one system in charge. Many performance plugins ship a "disable WordPress core lazy load" option specifically so their engine does not collide with core's. Check that you have not accidentally enabled lazy loading in two places at once, for example in both Jetpack and a caching plugin.

Mistake 6: Heavy blur-up placeholders that cost more than they save

LQIP (Low Quality Image Placeholder) and blur-up effects look slick: a blurred thumbnail fades into the sharp image. But many implementations inline a base64 placeholder into the HTML for every image, inflating the document size, and drive the transition with extra JavaScript. For a gallery-heavy page you can add tens of kilobytes of placeholder data to your initial HTML payload, which is the one thing you most want to keep lean.

Optimole and Smush both offer LQIP-style effects. They can be worthwhile on image-rich, design-led sites, but treat them as a deliberate aesthetic choice, not a free performance win. If you are chasing a passing Core Web Vitals score, a plain reserved-space placeholder beats an animated blur almost every time.

Mistake 7: Forgetting that crawlers and SEO depend on real src

JavaScript-based lazy loading that hides the real image behind data-src can leave images invisible to tools that do not execute JavaScript fully, which matters for image search indexing and for social and link-preview scrapers reading your Open Graph image. Native loading="lazy" keeps a real src in the markup and sidesteps the issue entirely. If you must use JS lazy loading, make sure your most important images, your OG image, and anything you want in Google Images are excluded or have a discoverable source.

A sane configuration in 2026

Put together, the practical setup looks like this:

  1. Let WordPress core handle native lazy loading for in-content images. It already skips the first image and adds fetchpriority to the LCP candidate.
  2. If you use a performance plugin's lazy-load module, disable core's version to avoid double processing, and configure an above-the-fold exclusion so your hero loads eagerly.
  3. Always ship explicit width and height (or aspect-ratio) to protect CLS.
  4. Exclude logos, icons, SVGs, and anything in the first viewport.
  5. Treat blur-up placeholders as optional polish, not a requirement.
  6. Verify in the field with a real Lighthouse or PageSpeed Insights run, then confirm with real-user LCP, CLS, and INP data rather than trusting the plugin's dashboard.

Lazy loading is a precision tool, not a switch you flip to "on." The plugins that get it wrong treat every image identically and bolt on JavaScript the browser no longer needs. The ones worth using give you exclusion rules, respect core's defaults, and stay out of the way of your most important image.