Estela Franco (@guaca) is a web developer, Google Developer Expert in web technologies, and web performance specialist, currently working at Schneider Electric. Estela is passionate about page speed and jamstack, and loves sharing with the community. She lives in sunny Barcelona (Spain) with her husband and son.
Largest Contentful Paint (LCP) is a performance metric representing the user experience’s loading facet. It’s part of the Core Web Vitals program and measures the render time of the largest image or text block visible within the viewport. When the LCP element is an image, which happens 72% of the time on mobile devices according to the 2022 Web Almanac, it’s critical to ensure that it’s rendered as quickly as possible.
Lazy-loading is an effective technique we can use to delay non-critical resources at the beginning of the page load. However, a considerable problem occurs when we apply this technique to an LCP image. Lazy-loading prevents the browser from loading the image immediately because it takes time for it to realize that the image is in the viewport and needs to be loaded. According to some lab tests, this could cause a 15% regression in LCP performance. This might sound obvious for someone working on web performance, but the fact that nearly one in five web pages are doing it is a sign that it’s not very well understood by most other web developers.
Moreover, according to the Web Almanac analysis, WordPress seems to have a starring role in this horror story, even though a fix has been available since January 2022 in version 5.9. However, WordPress isn’t the only platform with this anti-pattern, and it’s unclear if it’s caused by WordPress itself or any plugins or themes that people might use:
One theory that needs more investigation is that plugins may be circumventing the safeguards built into WordPress core by injecting LCP images onto the page with the lazy-loading behavior.
This article tests that theory by exploring the HTTP Archive data to show the technologies that use this lazy-loading anti-pattern and investigating what might be the root cause.
TL;DR:
- Yes, WordPress has a starring role here, but not entirely because of WordPress itself. In fact, WordPress is explicitly aware of this LCP issue and has taken steps to fix it in its core. However, plugins and themes can overwrite this behavior.
- WordPress plugins and themes with lazy-loading features can’t determine if an image is above the fold or not. Hence, LCP images are also lazy-load by default, except if you exclude them manually or by adding specific classes or attributes to them.
- WordPress plugins and themes should work on improving their lazy-loading features or, at least, improve their information and documentation to make it easier to understand the actual behavior and how you can prevent your LCP images from being lazy-loaded.
- Shopify is the second technology with more pages lazy-loading their LCP images. They are also aware of this, as you can read here. Consider if a section will be above-the-fold, or ensuring the main product image and blog header image can be set to eager load on more stable pages can help to avoid this lazy-loading behavior.
The main lazy-loading players
For this analysis, I’ve used the latest available data on HTTP Archive, specifically the October 2022 run for mobile devices. And there are terabytes of data to go through. So, the first question to answer is which platforms are using this anti-pattern the most, both in absolute and relative numbers. And yes, we can confirm WordPress leads this behavior, but Shopify also has an important role here.
This chart shows the % of pages that use a particular technology and are lazy-loading their img-based LCP element, using the native loading attribute or a custom JS approach. It should be noted that this distribution can be partially attributed to the usage of the technology rather than the relative degree of the problem for the platform. It is therefore also interesting to look at the proportion of pages with an img-based LCP using a technology vs the number of those pages that lazy-load it.
In these two charts, we confirm that WordPress is the main contributor of the total number of pages lazy-loading their img-based LCP, but we also see that Shopify pages have the highest proportion of pages lazy-loading their img-based LCP.
So, let’s dive deeper.
Shopify
Looking at the Shopify pages, we see a clear correlation with one particular library: LazySizes. And looking at the data, 72% of the Shopify pages that lazy-load their img-based LCP use LazySizes.
LazySizes is a common (and legacy) library used by many Shopify themes that may generate this behavior. And Shopify is already aware of this lazy-loading issue through their themes, as you can read in this article by Sia Karamalegos (kudos to Sia!). And she points out that part of this is due to the nature of how Shopify themes are built, with sections designed to be reusable across pages and in different locations on a page. In short, I agree entirely with Sia’s recommendations in her article: considering if a section will be above the fold or ensuring the main product image and blog header image can be set to eager load on more stable pages can help to avoid this lazy-loading behavior.
WordPress
The WordPress scenario is quite different. In this case, there isn’t a single plugin or library that might be causing this lazy-loading behavior in most cases. However, we see a familiar dependency: LazySizes.
The LazySizes library seems to be a dependency for some performance plugins, such as EWWW Image Optimizer and Autoptimize. So let’s explore the plugin universe and how they contribute to lazy-loading behaviors.
Plugins
If we look at the following chart, we see a correlation between using these plugins and lazy-loading an img-based LCP. Let’s go through the list and their documentation to determine whether those plugins can be responsible for this behavior.
These seven plugins account for half of all lazy-loading usage in WordPress. But it’s not that easy; WordPress is complex and there are plenty of pages combining several of those components. Thus, it can also depend on the combination of plugins and on the combination of plugins + themes/page builders.
Hence, if we compare the proportion of WordPress sites lazy-loading their LCP images when using these identified plugins or not, we see that WordPress sites that don’t use any of the identified plugins have a smaller percentage of lazy-loading LCP images:
Now that we’ve identified a few plugins that stand out in the results, let’s take a closer look at each of them to get a better understanding of how and why they may be resulting in lazy-loaded LCP images.
Slider Revolution
Slider Revolution is a plugin that allows you to easily add sliders and carousels to your WordPress site. And according to its documentation, it also allows you to lazy load the images used in those sliders and carousels.
I’m not sure if the documentation is correct, but it seems you can’t set any custom option to prevent the current slide from being lazy-loaded, which is the opposite of the desired behavior.
WP Rocket
WP Rocket is a well-known WordPress caching plugin that also allows you to implement up to “80% of web performance best practices”, such as lazy-loading images. If you activate this feature, it’s important to notice that you need to manually exclude above-the-fold images through their “images and iframe exclusions box” by targeting specific image names or classes.
If developers enable the lazy-loading feature and don’t notice that, above-the-fold images will be lazy-loaded, which could explain the high percentage of WP Rocket pages lazy-loading their img-based LCP.
Lightspeed Cache
Litespeed Cache is a WordPress cache plugin that also lets you implement some web performance best practices, such as lazy-load images. According to its documentation, when enabling the “Lazy Load images” feature, this setting only loads the images once they are visible in the viewport. However, it’s unclear if all images are lazy-loaded and the browser will load them once it discovers they are in the above-the-fold content—with the undesirable behavior we already know.
One more time, reading the documentation, you will discover that you can manually add exceptions to avoid this behavior:
W3 Total Cache
W3 Total Cache is a WordPress cache plugin that also offers several web performance features, such as “Defer offscreen images using Lazy Load to improve the user experience”.
I haven’t been able to easily identify the exact file responsible for this behavior (you can browse the code if you want), but based on the findings from other plugins this feature might be the root cause for this anti-pattern when using this particular plugin.
Autoptimize
Autoptimize is a WordPress plugin that allows you to implement many web performance best practices, such as minify and cache scripts, inline Critical-CSS, move and defer scripts to the footer, or lazy-load images. And, while reading their autoptimizeImages.php code you will recognize an external and familiar dependency: LazySizes.
It’s interesting to see that there are some by-default exceptions:
However, if developers are not aware of this, it makes sense that using LazySizes through Autoptimize may cause lazy-loading as we also saw on Shopify pages.
Note: In our analysis (here and here), we only looked for the presence of native and custom lazy-loading attributes (e.g. loading and data-src) but we did not account for exclusionary attributes like data-no-lazy. We expect these exceptions to be rare and to not have a significant effect on our results, but further analysis would be needed to verify that.
EWWW Image Optimizer
EWWW Image Optimizer is a WordPress plugin that allows you to optimize your images, in terms of format and compression, but also lets you to lazy-load them as a web performance best practice. One more time, when checking its code, we can see a LazySizes dependency:
And going deeper through their documentation, we see an explanation about how to NOT lazy load images above-the-fold:
Again, developers need to be aware of this to avoid lazy-loading img-based LCP elements when using this plugin.
Themes and Page Builders
Apart from the plugins, themes and page builders can also bring some lazy-load features. But there are thousands of themes and thousands of combinations of those themes with the mentioned plugins. I’ve tried to isolate the usage of some of the most used themes and page builders detected by Wappalyzer that don’t use any of the mentioned plugins. In other words, I’m trying to identify if there are any themes or page builders with a huge percentage of pages lazy-loading their LCP images without any help from the already-identified plugins.
Note that the “Twenty .*” theme aggregates the data from all the developed-by-WordPress themes starting with “Twenty” (ie Twenty Twenty-One). It’s important to note that this data doesn’t include the newer Twenty Twenty-Two or Twenty Twenty-Three themes, which might be a significant blind spot—another analysis would be necessary to properly identify them.
However, based on the limited list of themes and page builders, we identify Elementor (page builder) as the main theme or page builder related to more pages lazy-loading their LCP images.
And by surfing the Elementor website we can see they offer Lazy Loading features, which gives us a hint of the probable cause of this huge number of pages with this anti-pattern.
However, when looking at the proportion, we can find some other interesting insights:
Now it’s more evident that the Astra theme also has a high correlation with lazy-loading their LCP images. And, by checking Astra’s website, we can also notice they offer a lazy-load feature. Based on the prior examples, this might trigger that undesired lazy-load behavior by also lazy-loading the above-the-fold images.
Conclusions and recommendations
As you already know, the web ecosystem is huge and complex. And we see uncountable combinations of technologies, plugins, libraries and themes that might apply the lazy-loading behavior, even if we diligently set the proper configuration for one or some of them. Even still, the HTTP Archive data relies on Wappalyzer to identify the most used plugins, libraries and themes, so we’re not able to identify all of the possible technologies that can trigger this undesirable behavior.
However, through the analyzed data, we’ve been able to identify some likely culprits within the two major platforms that may be causing LCP images to be lazy-loaded. We’ve seen external dependencies as the more probable explanation for both Shopify and WordPress sites.
After this exciting journey, and based on the shown data, my four takeaways are:
- Always test that you’re not lazy-loading your LCP images and eliminate any LCP resource load delay. If you can’t or don’t want to do it manually, you can leverage tools like Lighthouse CLI and CI to programmatically check if the LCP element was lazy-loaded.
- If you’re a developer, don’t rely on external dependencies without reading their entire documentation. We’ve seen in prior examples you may need to manually add exclusions to ensure your LCP images are not lazy-loaded. This is not the ideal scenario, I know. But if themes/plugins/dependencies maintainers don’t improve this from their side, this is the only way to ensure you properly configure everything to avoid lazy-loading your LCP images.
- If you use Shopify, pay special attention to your above-the-fold sections to properly configure them and avoid any lazy-loading behavior on them, whether you use LazySizes, other libraries, or the native loading attribute.
- If you use WordPress, check all the plugins and themes that can add a lazy-load feature, read the documentation, and adapt your configuration to ensure you don’t lazy-load your LCP images.
- If you maintain any libraries or themes that lazy-load images, work on avoiding this anti-pattern for the above-the-fold images—especially LCP images. Moreover, if you can’t avoid it, clarify your basic information and documentation to ensure developers can quickly identify how to prevent this undesirable behavior.
- The easiest answer is not always the right one: Shopify and WordPress seemed to be responsible for this anti-pattern, but (harder to find) data helped us to understand that the right answer seems to be related to third party dependencies. WordPress and Shopify are explicitly aware of this LCP issue and WordPress has even taken steps to fix it in its core. However, we’ve seen plugins and themes can overwrite this behavior.