Make WordPress Core

Opened 21 months ago

Closed 5 weeks ago

Last modified 5 weeks ago

#58631 closed defect (bug) (invalid)

decoding="async" is added wildly, even if loading="eager" is there

Reported by: vyskoczilova's profile vyskoczilova Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version: 6.1
Component: Media Keywords: reporter-feedback
Focuses: performance Cc:

Description

Could we do something about decoding="async" parameter being added wildly on EVERY image?

It might seem to be a good idea, but it's not. I'm developing custom themes, and although it was introduced in 6.1, it touched me probably with some changes in 6.2 where wp_filter_content_tags() is replacing images without the parameter (and wp_get_attachment_image_attributes filter is not called in this case. I was using loading="eager" for my hero images (rendered in the template via Timber) which should be a no-no to adding decoding="async" for that.

I might help to fix the issue if somebody guides me through track and svn (but I'm fluent in git repos if there is finally a way to contribute this way).

Suggesting - checking for fetch priority and loading=eager params, and if present, do not add decoding="async" or loading="lazy."

I'm currently doing this, but it's silly to be fixing it on every site:

<?php
add_filter( 'wp_content_img_tag', function( $filtered_image, $context, $attachment_id ) {

    // search in the filtered_image html string for loading="eager" and if present, remove decoding="async"
    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 );

Change History (8)

#1 @vyskoczilova
21 months ago

I forgot to mention that it has a huge impact on LCP (and can't find edit button)

#3 @spacedmonkey
21 months ago

CC @flixos90

#4 @vyskoczilova
21 months ago

@Presskopp Thanks! Will look at it next week.

#5 @westonruter
8 weeks ago

  • Component changed from General to Media
  • Focuses performance added
  • Keywords reporter-feedback added
  • Version changed from 6.2.2 to 6.1

@vyskoczilova Hi! Just seeing this ticket now. I learned about your blog post The Unintended Consequences of decoding=’async’ on Your WordPress site’s LCP and then this ticket from a support topic from @guillermo77 for the Image Prioritizer plugin. I left a comment on the support topic, but it is held in moderation, so I'll reproduce it here.

I did some benchmarking with the Twenty Twenty-One theme with a desktop viewport (1350×940) on a post singular view there is an Image block at the beginning of the content with “Full width” alignment so the largest image size will be used. I used LocalWP for my test environment. By default, WordPress renders this on the frontend as:

<img
  fetchpriority="high"
  decoding="async"
  width="1024"
  height="668"
  src="http://localhost:10048/wp-content/uploads/2025/01/American_bison_k5680-1-1024x668.jpg"
  alt=""
  class="wp-image-7"
  srcset="
    http://localhost:10048/wp-content/uploads/2025/01/American_bison_k5680-1-1024x668.jpg  1024w,
    http://localhost:10048/wp-content/uploads/2025/01/American_bison_k5680-1-300x196.jpg    300w,
    http://localhost:10048/wp-content/uploads/2025/01/American_bison_k5680-1-768x501.jpg    768w,
    http://localhost:10048/wp-content/uploads/2025/01/American_bison_k5680-1-1536x1002.jpg 1536w,
    http://localhost:10048/wp-content/uploads/2025/01/American_bison_k5680-1-2048x1336.jpg 2048w,
    http://localhost:10048/wp-content/uploads/2025/01/American_bison_k5680-1-1568x1023.jpg 1568w
  "
  sizes="(max-width: 1024px) 100vw, 1024px"
>

Note the fetchpriority=high here which is correct since it is the LCP element. I also added a plugin to this site to allow manipulation of the decoding=async via a query parameter:

<?php
if ( isset( $_GET['add_decoding_attr'] ) ) {
        add_filter(
                'wp_img_tag_add_decoding_attr',
                static function () {
                        return rest_sanitize_boolean( $_GET['add_decoding_attr'] );
                }
        );
}

Then I used the benchmark-web-vitals command to make 250 requests to the URL with decoding=async removed and 250 requests with it added.

With a file decoding-async-urls.txt:

http://localhost:10048/lcp-image/?add_decoding_attr=false
http://localhost:10048/lcp-image/?add_decoding_attr=true

I ran this command:

npm run research -- benchmark-web-vitals --number=250 --file=decoding-async-urls.txt --output=csv --window-viewport=desktop --show-variance --show-percentiles

See the results in this Google Sheet. You can see from the results that the median LCP-TTFB metric (highlighted in bold) shows that adding decoding=async actually improves (reduces) LCP by 6.7%. Without the attribute the metric was 72.95 ms, when the attribute was added the LCP decreased to 68.05 ms. (Note this "TTFB-LCP" metric just subtracts TTFB from the LCP so that variations in TTFB are ignored from the metric.)

See also the original investigation by @mihai2u on the Trac ticket (#53232) for adding decoding=async by default where he found no negative impact when this attribute is added to the LCP IMG element. (Hey!)

Could you share more details about how you are seeing that decoding=async is negatively impacting your LCP? Can you provide some similar benchmarks and a test case to reproduce the issue?

If there is conclusive evidence that adding decoding=async hurts LCP performance, then core definitely should not be adding this to the IMG it adds fetchpriority=high to (in the same way it omits loading=lazy). However, we haven’t seen this to be the case.

This ticket was mentioned in Slack in #core-performance by westonruter. View the logs.


8 weeks ago

#7 follow-up: @vyskoczilova
5 weeks ago

  • Resolution set to invalid
  • Status changed from new to closed

Hi @westonruter ,

Thank you for your thorough analysis and the effort you put into benchmarking this. I really appreciate the data-driven approach, and based on your results, it looks like you’re right.

Unfortunately, I don’t have the time right now to run additional tests that might provide counter-evidence. It’s possible that the issues I observed were coincidental or influenced by other factors and I can’t find any notes if I took any. If I come across more concrete evidence in the future, I’ll be sure to revisit this topic and share my findings.

Thanks again for your work on this!

Best,
Karolina

#8 in reply to: ↑ 7 @westonruter
5 weeks ago

@vyskoczilova Would you mind updating your blog post with a note directing visitors to this Trac ticket? I don't want people to be thinking that WordPress is not trying to follow best performance practices. Thanks!

Note: See TracTickets for help on using tickets.