Lazy-loading images in 5.5

In WordPress 5.5, images will be lazy-loaded by default, using the native HTML loading attribute which became a web standard earlier in 2020. This will drastically save bandwidth on both servers as well as user agents across sites where images further down the page used to be loaded right away, even in the case the user might never scroll towards them.

By default, WordPress will add loading="lazy" to all img tags that have width and height attributes present. Technically this is handled on page output, similar to how responsive images are facilitated in WordPress by adding srcset and sizes attributes. To improve server-side performance of the two features, a new wp_filter_content_tags() function has been introduced so that img tags only need to be parsed once while then deferring the modifications to more specific functions related to the feature.

See #44427 for the overarching TracTrac An open source project by Edgewall Software that serves as a bug tracker and project management tool for WordPress. ticketticket Created for both bug reports and feature development on the bug tracker..

Reduced layout shifting as a prerequisite

A common user experience problem in modern website is so-called layout shifting, often caused by slow-loading media resources like images: By default, only after an image is loaded, the browser can layout the page correctly, which results in the content e.g. below the image to shift. This issue can be easily resolved by providing width and height attributes on img tags, as the browser will use them to determine the aspect ratio of the image so that it can infer the page layout ahead of actually loading the image.

While this is already a major problem without lazy-loading images, with lazy-loading it becomes more relevant. Therefore WordPress will only add loading="lazy" to img tags which have both dimension attributes present. At the same time, resolving the underlying issue is just as important to reduce layout shifting in general, which is why with version 5.5 WordPress will start back-filling width and height attributes on img tags when they are not already present. To do that, it reuses the established logic already in place for determining srcset and sizes attributes. Like with those attributes, width and height can only be determined if an image is for a WordPress attachment and if the img tagtag A directory in Subversion. WordPress uses tags to store a single snapshot of a version (3.6, 3.6.1, etc.), the common convention of tags in version control systems. (Not to be confused with post tags.) includes the relevant wp-image-$id class.

WordPress has mostly been following this best practice, and work is being done to ensure all images in the editor will have width and height. Back-filling these attributes should not have any implications on themes, as long as a theme’s CSSCSS Cascading Style Sheets. works appropriately with classic editor content., which is expected: If an image’s width or height is modified via CSS, the respective other attribute should be set to auto, to avoid the image from being stretched.

See #50367 for further background information on this change.

Customizing lazy-loading

By default, WordPress will add a loading="lazy" attribute to the following images:

  • images within post content (the_content)
  • images within post excerpts (the_excerpt)
  • images within text widgets (widget_text_content)
  • avatarAvatar An avatar is an image or illustration that specifically refers to a character that represents an online user. It’s usually a square box that appears next to the user’s name. images (get_avatar)
  • template images using wp_get_attachment_image() (wp_get_attachment_image)

Developers can customize this behavior through various filters, the most foundational one being wp_lazy_loading_enabled, which receives the following parameters:

  • $default: The boolean default of true to filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output..
  • $tag_name: An HTMLHTML HyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers. tag name. While per current WordPress behavior this will always be img, it should be noted that the loading attribute is a generic attribute and may be expanded to support further elements, e.g. iframes, in the future.
  • $context: A context string as additional parameters, indicating where the image technically comes from, usually a WordPress hook name. Based on how WordPress itself uses lazy-loading, the context can be one of the five values in parentheses in the list above.

For example, if you would like to turn off lazy-loading by default for template images, you could use the following code snippet:

function disable_template_image_lazy_loading( $default, $tag_name, $context ) {
	if ( 'img' === $tag_name && 'wp_get_attachment_image' === $context ) {
		return false;
	}
	return $default;
}
add_filter(
	'wp_lazy_loading_enabled',
	'disable_template_image_lazy_loading',
	10,
	3
);

In order to modify the loading attribute for very specific images, there are two different approaches, depending on the type of images:

For images that appear within a content blob (e.g. the_content, the_excerpt, widget_text_content), another new filter wp_img_tag_add_loading_attr can be used, which receives the following parameters:

  • $value: The loading attribute value, either “lazy” (default), “eager”, or false. If you want to disable lazy-loading for an image, it is strongly recommended to specify false so that the attribute is omitted altogether.
  • $image: The entire image HTML tag with its attributes.
  • $context: The context, similar as described for the other filter above.

For example, if you would like to disable lazy-loading for a specific attachment image with ID 42 in size “large” within post content, you could use the following code snippet:

function skip_loading_lazy_image_42_large( $value, $image, $context ) {
	if ( 'the_content' === $context ) {
		$image_url = wp_get_attachment_image_url( 42, 'large' );
		if ( false !== strpos( $image, ' src="' . $image_url . '"' ) {
			return false;
		}
	}
	return $value;
}
add_filter(
	'wp_img_tag_add_loading_attr',
	'skip_loading_lazy_image_42_large',
	10,
	3
);

For images which are output via wp_get_attachment_image(), the attribute can simply be controlled through the function’s $attr parameter, which can be the same possibles values like the $value parameter for the above filter. In order to not lazy-load an image, an attribute value of false should be specified, which will result in the attribute being omitted. For example:

echo wp_get_attachment_image(
	42,
	'large',
	false,
	array( 'loading' => false ),
);

Theme developers are recommended to granularly handle loading attributes for images anytime they rely on wp_get_attachment_image() or another function based on it (such as the_post_thumbnail() or get_custom_logo()), depending on where they are used within templates. For example, if an image is placed within the header.php template and is very likely to be in the initial viewport, it is advisable to skip the loading attribute for that image.

Images that are marked as candidates for lazy-loading require the browser to resolve where the image is positioned on the page, which relies on the IntersectionObserver to be available and thus as of today slightly delays their fetching. Experiments using Chrome for Android have shown that the impact of such loading=”lazy” images in the initial viewport on the Largest Contentful Paint metric is fairly small, with a regressionregression A software bug that breaks or degrades something that previously worked. Regressions are often treated as critical bugs or blockers. Recent regressions may be given higher priorities. A "3.6 regression" would be a bug in 3.6 that worked as intended in 3.5. of <1% at the 75th and 99th percentiles compared to non lazy-loaded images – yet it is a consideration to make where theme developers can apply some fine tuning for even better user experience.

See #50425 for further background information on this change.

Browser compatibility

The loading attribute is widely supported by modern browsers, with an increasing trend: For example, while Safari support is not yet available at the time of publication, the feature is being worked on there as well and has already been merged into the underlying WebKit engine.

Yet, even browsers that currently do not support the loading attribute will not see any negative consequences from WordPress providing the attribute on images, since the native lazy-loading mechanism is implemented as a fully progressive enhancementenhancement Enhancements are simple improvements to WordPress, such as the addition of a hook, a new feature, or an improvement to an existing feature.: For those browsers the attribute will simply be ignored. This also means that whenever a browser implements support for the feature, its users will get the benefits right away when they browse WordPress-powered sites.

Props @azaozz for helping with this post.

#5-5, #dev-notes, #feature-lazyloading