Enhanced lazy-loading performance in 5.9

Lazy-loading images was introduced in WordPress 5.5 and later expanded to also cover iframes in WordPress 5.7. In the upcoming WordPress 5.9 release, the implementation for both has received some fine tuning to improve performance.

A performance analysis from mid-2021 had discovered that the lazy-loading implementation from WordPress had been causing a slight performance regression in the Largest Contentful Paint metric (LCP). As outlined in the referenced post, the reason for that was that images and iframes in the initial viewport would be marked to lazy-load them, since the WordPress implementation would mark nearly all images with loading="lazy". The aforementioned post then further explains that this can be improved by skipping addition of loading="lazy" for the first content image or iframeiframe iFrame is an acronym for an inline frame. An iFrame is used inside a webpage to load another HTML document and render it. This HTML document may also contain JavaScript and/or CSS which is loaded at the time when iframe tag is parsed by the user’s browser., which in the vast majority of cases will appear within the initial viewport. WordPress can only make educated guesses around that and not be 100% certain, but an analysis taking into account 50 popular themes showed that the enhancementenhancement Enhancements are simple improvements to WordPress, such as the addition of a hook, a new feature, or an improvement to an existing feature. brought LCP improvements across the board, up to 30% faster page load. WordPress 5.9 now comes with this refinement implemented, leading to the corresponding enhanced LCP performance.

How it works

So far, fine tuning which images and iframes should be lazy-loaded or not has been possible using the wp_img_tag_add_loading_attr and wp_iframe_tag_add_loading_attr filters, and these are still available as before. However, to improve the performance out-of-the-box without requiring a developer to customize the behavior, WordPress will now skip the very first “content image or iframe” on the page from being lazy-loaded. The term “content image or iframe” here denotes any image or iframe that is found within content of any post in the current main query loopLoop The Loop is PHP code used by WordPress to display posts. Using The Loop, WordPress processes each post to be displayed on the current page, and formats it according to how it matches specified criteria within The Loop tags. Any HTML or PHP code in the Loop will be processed on each post. https://codex.wordpress.org/The_Loop. as well any featured imageFeatured image A featured image is the main image used on your blog archive page and is pulled when the post or page is shared on social media. The image can be used to display in widget areas on your site or in a summary list of posts. of such a post. This applies to both “singular” and “archive” content: In a “singular” context the first image or iframe of the (only) post is not lazy-loaded, while in an “archive” context the first image or iframe of the first post in the query is not lazy-loaded.

Customizing the behavior

The approach of not lazy-loading the first content image or iframe enhances LCP performance correctly for the majority of themes, which use a single-column layout for post content. For themes with alternative layouts such as multi-column, a new wp_omit_loading_attr_threshold 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. is now available and can be used to change how many of the first images/iframes should be skipped from being lazy-loaded – per the above, the default is 1. It is recommended for theme authors to use this filter in cases where based on the layout it is likely to have more than one content image/iframe in the initial viewport.

For example, a theme that uses a three-column grid of posts in its archives could leverage the filter to override the threshold to 3 on archive pages, which would then result in the first three content images/iframes not being lazy-loaded. The following code snippet illustrates what that could look like:

function skip_lazyloading_on_first_three_archive_images( $omit_threshold ) {
    if ( is_home() || is_archive() ) {
        return 3;
    }
    return $omit_threshold;
}
add_filter( 'wp_omit_loading_attr_threshold', 'skip_lazyloading_on_first_three_archive_images' );

The refinement of the lazy-loading implementation should notably improve LCP performance for most sites that rely on it, while not having adverse effects for sites where the default heuristics described above do not apply to. That is only a solid starting point though. In the future, specifically with the more semantic content specification that blockBlock Block is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience.-based themes will facilitate, we will be able to further fine tune the lazy-loading implementation by using the available block information.

For more background information on the changes described above, see #53675.

Props to @adamsilverstein and @audrasjb for review and proofreading.

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