Image performance enhancements in WordPress 6.3

WordPress 6.3 comes with several enhancements that improve load time performance for content with images. The benefits can be seen in the Largest Contentful Paint metric (short “LCP”), which captures the time from the beginning of the request until the largest content element in the viewport has been rendered.

Summary of the changes

At a high level, the changes can be summarized as follows:

  • WordPress now automatically adds the fetchpriority attribute with a value of “high” to the image it determines most likely to be the “LCP image”, i.e. the image that is the largest content element in the viewport. The attribute tells the browser to prioritize this image, even before it has computed the layout, which typically improves LCP by 5-10%. See the original proposal post for additional context.
  • Further adjustments and fixes have been implemented to improve the automatic handling of lazy-loading via the loading attribute to more reliably detect when to omit the attribute from some images. This effort was started in WordPress 5.9 and continued in WordPress 6.2. Most recently, a holistic assessment of the remaining issues led to all of them being fixed in WordPress 6.3. See the relevant WordPress 5.9 dev note post for additional context on why not lazy-loading in-viewport images, especially the LCP image, is important for performance.

In order to implement the automated fetchpriority support, some refactoring was needed to decouple the logic for detecting in-viewport images from the lazy-loading specific functionality. As a result, the fetchpriority and loading attributes are now controlled by a single function in WordPress coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress.. If you have been relying on specific parts of WordPress core’s lazy-loading logic in your pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party or theme, please continue reading to learn more about those changes.

New function wp_get_loading_optimization_attributes()

A new function wp_get_loading_optimization_attributes( string $tag_name, array $attr, string $context ) is introduced in WordPress 6.3, which returns an associative array of additional attributes and their values. This provides a central place to get HTMLHTML HyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers. attributes that potentially improve the load time performance of images. For now, the function can only return a fetchpriority or loading attribute (or neither if not applicable), though this may be enhanced with other performance-related attributes in the future.

The new function is now used everywhere in WordPress core where fetchpriority or loading attributes are handled for images, most importantly:

  • Handling in-content images, via wp_filter_content_tags()
  • Handling programmatically rendered images, via wp_get_attachment_image() / get_the_post_thumbnail()
  • Handling 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, via get_avatar()
  • Handling the custom headerHeader The header of your site is typically the first thing people will experience. The masthead or header art located across the top of your page is part of the look and feel of your website. It can influence a visitor’s opinion about your content and you/ your organization’s brand. It may also look different on different screen sizes. image, via get_header_image_tag() (support added via #58680)
  • Handling images within shortcodes, via do_shortcode() (support added via #58681)
  • Handling images in the image widgetWidget A WordPress Widget is a small block that performs a specific function. You can add these widgets in sidebars also known as widget-ready areas on your web page. WordPress widgets were originally created to provide a simple and easy-to-use way of giving design and structure control of the WordPress theme to the user., via the WP_Widget_Media_Image class (support added via #58704)

That by itself is an improvement already, leading to consistent behavior across the board. Prior to this change, the above functions were using slightly different implementations for lazy-loading which could potentially lead to issues.

In order to use the function, pass the HTML element’s 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.) name and attributes to the new function, alongside a context string depending on how the image is being rendered in WordPress (for example “the_content”, or “wp_get_attachment_image”). Providing the tag name is important as the function is not limited to only images. At the moment it supports “img” tags (for fetchpriority and loading attributes) and “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.” tags (for loading attributes), though this may be expanded in the future. The return value of the function is always an array, which can be merged into the tag’s existing array of attributes, or alternatively specific attribute values can be extracted as needed.

Here you can see an example on how to potentially use the function:

$attr = array(
	'src'    => 'my-image.jpg',
	'width'  => 500,
	'height' => 300,
);
$attr = array_merge(
	$attr,
	wp_get_loading_optimization_attributes( 'img', $attr, 'wp_get_attachment_image' )
);
echo '<img';
foreach ( $attr as $key => $value ) {
	echo ' ' . sanitize_key( $key ) . '="' . esc_attr( $value ) . '"';
}
echo '>';

Note that the example uses the “wp_get_attachment_image” context, which is typically used only in the wp_get_attachment_image() function. While you could provide another arbitrary value here, at the moment it is advisable to use the most suitable core context as WordPress core only handles those specifically. Assuming that this is a generic image being rendered, the “wp_get_attachment_image” context works well.

Which additional attributes are returned in the above example depend on the specific context and place where the function is called. If the image is the first large image rendered in the response, the return value would be array( 'fetchpriority' => 'high' ). If it is an image that is likely further down the page, the return value would be array( 'loading' => 'lazy' ). There is also a chance that the array would be empty; for example, if the image is likely in the viewport but not large enough to be eligible as the LCP image.

Note that the function will never return both fetchpriority="high" and loading="lazy" for the same image, as those two attribute-value combinations are mutually exclusive and should never be used on the same element: lazy-loading an image while also marking it as high priority is an anti-pattern that should be avoided.

Rendering a custom header image in your theme

Custom header images are a feature that classic themes have supported for many years. With WordPress 6.3, the get_header_image_tag() function has received support to automatically include relevant loading optimization attributes, using the new wp_get_loading_optimization_attributes() function.

If you are developing or maintaining a theme that has custom header image support, it is advisable to use the get_header_image_tag() function to render the image. If you’re concerned about supporting WordPress versions prior to when this function was introduced in WordPress 4.4, see #58675 for how older core themes are updated to use this function when available.

If for some reason you are unable to use the get_header_image_tag() function and need to render the image tag manually while retrieving the image URLURL A specific web address of a website or web page on the Internet, such as a website’s URL www.wordpress.org with the get_header_image() function, you can still ensure the loading optimization attributes are applied. For example consider the following code:

$attr = array(
	'src'    => get_header_image(),
	'width'  => (int) get_custom_header()->width,
	'height' => (int) get_custom_header()->height,
);
$attr = array_merge(
	$attr,
	wp_get_loading_optimization_attributes( 'img', $attr, 'get_header_image_tag' )
);
echo '<img';
foreach ( $attr as $key => $value ) {
	echo ' ' . sanitize_key( $key ) . '="' . esc_attr( $value ) . '"';
}
echo '>';

Additionally, if you are certain that the header image is also the LCP image of the page, you can manually mark it as such, by adding 'fetchpriority' => 'high' to the $attr array before calling wp_get_loading_optimization_attributes(). Calling the function would still be crucial to ensure WordPress core considers the image so that subsequent images on the page also receive the correct attributes.

Customization of image priority and lazy-loading behavior

With the new function being used consistently anywhere images are rendered in WordPress core, support for customizing is also improved. The function will never override attributes that are already provided, so if you set a fetchpriority or loading attribute on an image before this function is called, the attribute will be kept as is. This allows fine tuning by not enforcing the default automated behavior. If doing so, keep in mind never to set both fetchpriority="high" and loading="lazy" for an element. If the function encounters those two attribute-value combinations together, it will trigger a warning.

Modifying the default behavior for lazy-loading works just like before. Relevant filters like wp_lazy_loading_enabled, wp_img_tag_add_loading_attr, and wp_omit_loading_attr_threshold have not been modified in this release. However, the default value for the 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. has been modified from 1 to 3 (see #58213 for context) in order to better support the common pattern of multi-column layouts with images. Please refer to the lazy-loading dev notedev note Each important change in WordPress Core is documented in a developers note, (usually called dev note). Good dev notes generally include a description of the change, the decision that led to this change, and a description of how developers are supposed to work with that change. Dev notes are published on Make/Core blog during the beta phase of WordPress release cycle. Publishing dev notes is particularly important when plugin/theme authors and WordPress developers need to be aware of those changes.In general, all dev notes are compiled into a Field Guide at the beginning of the release candidate phase. posts for WordPress 5.5 and for WordPress 5.9 for more information on those existing filters.

For fetchpriority, a new filter wp_min_priority_img_pixels was introduced, which allows developers to modify the size threshold above which an image is considered eligible to receive fetchpriority="high". This minimum threshold value is in place to ensure only images of a certain size are considered for the LCP image. For example, a small icon would never be the LCP image — even if there were no other images above the fold, the LCP element would most likely be a non-image element, e.g. a heading.

The size is defined as a the product of “width * height” of the image, and the default threshold is 50,000. For example, this means that an image with a width of 300 pixels and height of 200 pixels is eligible (since 300*200=60000) while an image with a width of 200 pixels and height of 200 pixels is not (since 200*200=40000).

Here is an example: Imagine you have an image that is 200×200 pixels, and despite being that small, you know it is the LCP image of the specific URL it appears on. You could then use the new filter to ensure the image can receive fetchpriority="high".

add_filter(
	'wp_min_priority_img_pixels',
	function( $size ) {
		if ( is_this_the_url_with_the_image() ) {
			return 200 * 200; // Images at least as big as 200x200 
		}
		return $size;
	}
);

Keep in mind that in such a situation you could alternatively provide fetchpriority="high" on the image manually, which is just another arguably more direct way to achieve the same result. Therefore this approach is better suited for dynamic content in layouts where you might not know the exact image.

Deprecated functions

With the new wp_get_loading_optimization_attributes() function controlling both fetchpriority and loading attributes, a few existing functions are being deprecated as part of the WordPress 6.3 release.

wp_get_loading_attr_default() is superseded by the new function and therefore should not be used anymore. If you are currently calling this function in your plugin, you can use code such as the one below to remain compatible with WordPress versions before and after this deprecation:

function myplugin_get_loading_attr_default( $tag_name, $attr, $context ) {
	// For WP >= 6.3.
	if ( function_exists( 'wp_get_loading_optimization_attributes' ) ) {
		$loading_optimization_attr = wp_get_loading_optimization_attributes( $tag_name, $attr, $context );
		if ( isset( $loading_optimization_attr['loading'] ) ) {
			return $loading_optimization_attr['loading'];
		}
		return false;
	}

	// For WP < 6.3.
	return wp_get_loading_attr_default( $context );
}

While the above works well for a transition period, for the long term consider also supporting fetchpriority, e.g. by using the new function directly.

The other function that has been deprecated is wp_img_tag_add_loading_attr(), as this function is superseded by a new wp_img_tag_add_loading_optimization_attrs(), which also encompasses the fetchpriority enhancementenhancement Enhancements are simple improvements to WordPress, such as the addition of a hook, a new feature, or an improvement to an existing feature.. Note that both of these functions are intended for parsing HTML content from the database, so it is advisable for plugins to not rely on either of them. Try to use wp_get_loading_optimization_attributes() or a wrapper function like the above instead to add loading optimization attributes to a specific image.

Please see #58235 for more information on fetchpriority support.

Lazy-loading issues addressed

As mentioned before, WordPress 6.3 addresses several outstanding problems where loading="lazy" was being added to images that should not receive it. Other than the aforementioned change of the default value for the wp_omit_loading_attr_threshold filter from 1 to 3 (see #58213), those fixes do not affect the underlying developer APIs in any way.

Here is a list of the other lazy-loading bugs that were addressed:

  • Images in the header (before “the 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.”) in classic themes are now eligible for having the loading=”lazy” attribute omitted. See #58211.
    • For 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. themes, this was already fixed in WordPress 6.2, via #56930.
  • Programmatically rendered images which are injected into post content no longer incorrectly affect the lazy-loading logic. See #58089.
  • Rendering an excerptExcerpt An excerpt is the description of the blog post or page that will by default show on the blog archive page, in search results (SERPs), and on social media. With an SEO plugin, the excerpt may also be in that plugin’s metabox. where the full post contains images no longer incorrectly affects the lazy-loading logic. See #56588.
  • A duplicate call to get the loading attribute value in get_the_post_thumbnail() has been removed. See #58212.
  • Images before “the loop” are now counted towards the threshold for not lazy-loading. See #58635.

Props @westonruter @joemcgill for technical review, @mukesh27 @stevenlinx for proofreading.

#6-3, #dev-notes, #dev-notes6-3