Blog /

The Unintended Consequences of decoding=’async’ on Your WordPress site’s LCP

Explore the impact of WordPress’s decoding=”async” image optimization on your site’s LCP score and learn how to fix it.

In WordPress 6.1 and 6.2 there were introduced several “performance boost” changes. In particular, applying the decoding=”async” attribute for all images has become a significant problem for my and maybe your sites. This feature, intended as a performance boost, could harm the critical Largest Contentful Paint (LCP) metric.

Natively, WordPress adds the decoding="async" attribute to every image by default. This attribute tells the browser to decode the image asynchronously, allowing other tasks to run in the meantime. This might sound like a good thing for performance, but it can negatively impact LCP when not managed correctly. LCP is a critical Core Web Vital that measures when the main content of a webpage has finished rendering on the screen. Delaying image decoding, especially for images that contribute to the LCP, can result in a worse LCP score, affecting both the user experience and potentially your site’s SEO.

Understanding Decoding Types: Auto, Sync, Async

In modern web development, there are three types of image decoding: auto, sync, and async.

  1. auto: The browser decides when to decode the image based on its own heuristics. It’s the default behavior if the decoding attribute is not specified.
  2. sync: The image is decoded before it’s painted on the screen. This can cause layout jank if the image is large, but it guarantees it is ready once needed.
  3. async: The image decoding is delayed until other tasks have been handled, which can improve overall load times but may delay critical images.

In a perfectly optimized website, each image should have its decoding type set based on its importance to the page layout, visibility, and user interaction. However, by design, WordPress sets every image to async, irrespective of other parameters such as loading="eager" or fetchPriority="high", which can result in less-than-ideal LCP scores.

To solve this issue, we can use a couple of approaches to refine WordPress’s behavior. Both of these solutions involve removing the decoding attribute from images when they have certain other attributes set.

Snippets

Snippet 1: Modifying wp_get_attachment_attributes

Here, we’ll adjust the attributes directly when WordPress is generating the image HTML:


<?php
/**
* @snippet Remove decoding=async when fetchpriority=high, loading=eager or your custom class from WP handled images
* @article https://kybernaut.cz/en/clanky/the-unintended-consequences-of-decodingasync-on-your-wordpress-sites-lcp/
* @author Karolína Vyskočilová
* @compatible WordPress 6.1 and greater
* @donate $9 https://www.paypal.com/paypalme/KarolinaVyskocilova
*/
add_filter( 'wp_get_attachment_image_attributes', function( $attributes ) {
if ( isset( $attributes['fetchpriority'] ) && 'high' === $attributes['fetchpriority']
|| isset( $attributes['loading'] ) && 'eager' === $attributes['loading']
|| isset( $attributes['class'] ) && false !== strpos( $attributes['class'], 'your-custom-hero-image-class' )
) {
unset( $attributes['decoding'] );
}
return $attributes;
} );

This snippet hooks into WordPress’s image HTML generation process, checks for the fetchPriority and loading attributes, and removes the decoding attribute if necessary. There’s also an option to exclude certain images by adding a specific class to them.

Snippet 2: Post-Processing Rendered HTML

Alternatively, we could process the HTML after WordPress has generated it:


<?php
/**
* @snippet Remove decoding=async when fetchpriority=high, loading=eager in rendered HTML globally
* @article https://kybernaut.cz/en/clanky/the-unintended-consequences-of-decodingasync-on-your-wordpress-sites-lcp/
* @author Karolína Vyskočilová
* @compatible WordPress 6.1 and greater
* @donate $9 https://www.paypal.com/paypalme/KarolinaVyskocilova
*/
add_filter( 'wp_content_img_tag', function( $filtered_image, $context, $attachment_id ) {
if ( false !== strpos( $filtered_image, 'loading="eager"' ) || false !== stripos( $filtered_image, 'fetchpriority="high"' ) ) {
$filtered_image = str_replace( ' decoding="async"', '', $filtered_image );
}
return $filtered_image;
}, 10, 3 );

This snippet is a bit of a heavier solution, as it operates on the HTML after it’s already been rendered. However, it has the benefit of also working on images that weren’t handled by WordPress’s native functions, providing a more global solution.

Snippet 3: Disable decoding=async

Lastly, it’s important to note that there’s another way to tackle this problem: disabling adding decoding entirely. WordPress has a built-in filter that can be used to disable this feature. However, while it’s a simple solution, it’s also quite drastic and might lead to other performance issues, so use it with care.


<?php
add_filter('wp_img_tag_add_decoding_attr', '__return_false');

 

Remember, the goal here isn’t to demonize lazy loading or the decoding="async" attribute. They are powerful tools for improving web performance. The key is using them judiciously and ensuring they don’t inadvertently hurt the metrics that matter most for user experience and SEO, such as the Largest Contentful Paint. By understanding these aspects and making careful adjustments, you can harness their benefits without compromising your site’s performance.

P.S. If I saved you time please feel free to make a donation via my PayPal (invoice can be issued upon request). Any contribution, big or small, is greatly appreciated and helps keep these resources free for everyone. Thank you for your support!

P.P.S. Blog post is written with the help of Chat GPT-4.