Lazy-Loading Images in WordPress Core

Lazy-loading images has been a commonly used mechanism to significantly improve pageload performance for several years. For the WordPress ecosystem alone, there are a myriad of plugins that enable lazy-loading.

While historically lazy-loading images has required a custom JavaScript-based approach, there is now a native web solution which relies solely on the presence of a new loading attribute on img tags and provides a standardized user experience without content shifting. The HTML specification for the loading attribute is near completion and is already supported by several browsers, including Chrome and Edge.

Enabling lazy-loading for images in WordPress content was first proposed nearly two years ago, however the JavaScript implementation at the time would potentially have introduced many edge cases and failures. Using the new loading attribute removes these concerns. This post describes the suggested solution.

Performance Impact

According to HTTPArchive, images are the most requested asset type for most websites and usually take up more bandwidth than any other resource. At the 90th percentile, sites send about 4.7 MB of images on desktop and mobile.

Native lazy-loading for the web, web.dev

Without lazy-loading, all images on a web page are loaded immediately. This can significantly harm performance, especially on pages that contain many images. If several of these images are “below the fold”, i.e. the part of the page that the user does not immediately see, loading those images right away is unnecessary and potentially even wasteful of network resources: If the user never scrolls towards those images, they would have been loaded regardless.

Since lazy-loading has become a must-have performance improvement, the new loading attribute specification aims to standardize the behavior and make it even faster, integrated in the web platform.

With WordPress enabling native lazy-loading by default, it would significantly impact performance and user experience for millions of sites, without requiring any technical knowledge or even awareness of lazy-loading as a concept. Adopting the new loading attribute is a great chance for WordPress to lead the way for a faster web overall.

Technical Solution

The loading attribute currently supports two possible values:

  • eager, to load an image immediately on pageload
  • lazy, to load an image only when it becomes relevant for the viewport

The implementation seeks to enable lazy-loading images by default, providing the loading attribute with value lazy on the following img tags:

  • Images in post content
  • Images in post excerpts
  • Images in comments
  • Images in text widget content
  • Individual images rendered via wp_get_attachment_image()
  • Avatar images rendered via get_avatar()

Note that loading="lazy" will only be added if the respective tag does not yet include a loading attribute. In other words, to prevent an image from being lazy-loaded, it is recommended to specify loading="eager".

Customization for Developers

Note that the customization capabilities outlined below and how they work exactly is subject to change.

While the images outlined above will be lazy-loaded by default, developers will be able to override this behavior both globally and on a per-image basis.

A new filter wp_lazy_loading_enabled will allow turning the feature on and off. For example, one could disable lazy-loading entirely with the following snippet:

add_filter( 'wp_lazy_loading_enabled', '__return_false' );

This filter also passes a secondary parameter $tag_name, which is a specific tag name to enable or disable lazy-loading for, and $context, which typically is the name of the current filter being run. Currently, img is the only supported value, but since adding loading support to additional tags is on the horizon (e.g. some browsers already support the loading attribute on iframe tags), this parameter exists for future compatibility. For example, if you want to be more specific and disable lazy-loading only for images (so that future supported tags would by default have it enabled), you could use the following snippet:

add_filter(
	'wp_lazy_loading_enabled',
	function( $result, $tag_name ) {
		if ( 'img' === $tag_name ) {
			return false;
		}
		return $result;
	},
	10,
	2
);

In addition to this filter which allows customization across the entire site, there is another filter wp_set_image_loading_attr that filters the value of the loading attribute for individual control per image. The filter passes the full img tag markup including all attributes as second parameter, the full content blob that the image is part of, and the context, which typically is the current filter being run. wp_set_image_loading_attr can for example be used for interoperability by plugins that currently use alternative mechanisms to lazy-load, for example a class or a data attribute. It is recommended to only do this as a transition though, and in the long run update such plugins to specify loading="eager", in which case core will leave that in place as is, as mentioned before.

Please see the inline documentation in the plugin for more detail on how to customize lazy-loading behavior.

Call for Testing

The proposed solution is available as a feature plugin WP Lazy Loading in the plugin repository. The plugin is being developed on GitHub. Your testing and feedback will be much appreciated. Particularly testing interoperability with various content creation mechanisms (e.g. blocks, shortcodes) and existing lazy-loading plugins would be helpful. The current goal is to get this feature released as part of WordPress 5.4, merging in the next two weeks.

Please share your ideas, questions and thoughts either in a comment on this post, in the plugin’s support forum on wordpress.org, or on the GitHub repository.