Make WordPress Core

Opened 18 months ago

Closed 14 months ago

Last modified 11 months ago

#56930 closed defect (bug) (fixed)

Block themes: improve omitting lazy-loading attribute on first content image/iframe

Reported by: adamsilverstein's profile adamsilverstein Owned by: flixos90's profile flixos90
Milestone: 6.2 Priority: normal
Severity: normal Version: 5.9
Component: Media Keywords: has-patch has-unit-tests has-testing-info commit add-to-field-guide
Focuses: performance Cc:

Description

In #53675 we improved core's auto application of the loading="lazy" attribute for content images (and iframes). The behavior was adjusted to avoid adding the lazy loading attribute to the first content image, because that can actually slow down the speed/priority of that image being loaded. Additional details are on this post.

Subsequent testing of the feature revealed that block theme images still aren't handled correctly: all content images receive the lazy loading attribute, including the first one.

This is caused by the block theme "double" pass at the the_content (which can actually help performance ). To fix this we need to avoid "counting" images (which we do to skip adding the lazy attribute to the first image) the first pass by block themes where the $context is false . During the actual content pass, the context value is the_content.

Testing instructions:

  • Enable the twentytwentythree theme
  • Create a post with two different images and view the post
  • Note that currently both images have the loading="lazy" attribute

patch incoming, after this patch, the first image does not have the loading="lazy"attribute

  • test with a classic theme (eg twentyninetween) to verify the same expected behavior

Attachments (1)

56930.diff (728 bytes) - added by adamsilverstein 18 months ago.

Download all attachments as: .zip

Change History (55)

This ticket was mentioned in PR #3538 on WordPress/wordpress-develop by @adamsilverstein.


18 months ago
#1

  • Keywords has-patch added
  • Disable wp_lazy_loading_enabled by default when context is false. Prevents counting of the fist block theme content pass which resulted in actual content filtering setting all content images loading attribute to lazy

Testing instructions:

  • Enable the twentytwentythree theme
  • create a post with two different images and view the post
  • note that before this patch, both images have the loading="lazy" attribute
  • after this patch, _the first image does not have the loading="lazy"attribute_
  • test with a classic theme (eg twentyninetween) to verify the same expected behavior

Trac ticket: https://core.trac.wordpress.org/ticket/56930

#2 @ocean90
18 months ago

Duplicate/related: #56927, #55996

#3 @adamsilverstein
18 months ago

Thanks @ocean90 - I didn't see those. #56927 in particular would be fixed by this and could be considered a duplicate. The other one deals more broadly with the underlying change in behavior.

This ticket was mentioned in Slack in #core by mike. View the logs.


18 months ago

#5 @Bernhard Reiter
18 months ago

#56927 was marked as a duplicate.

#6 @adamsilverstein
18 months ago

@spacedmonkey pointed out https://github.com/WordPress/gutenberg/issues/45224 which also looks like a duplicate.

This ticket was mentioned in PR #3549 on WordPress/wordpress-develop by @flixos90.


18 months ago
#8

  • get_the_block_template_html() calls various (partially expensive) functions to parse the block template content.
  • A problem with that is that, within the block template, there usually are one or more instances of the post-content block, which itself parses post content using similar functions (which has historically been the established behavior).
  • This means that the post content blobs within the template content are now double-processed. Not only is this wasteful, more importantly it leads to various bugs (e.g. https://core.trac.wordpress.org/ticket/56930) and is unintuitive for plugin developers (e.g. if you use the wp_content_img_tag filter to make a modification, you now have to consider that this filter may run twice on the same piece of content).
  • This PR addresses this problem by doing the following:
    • After fully parsing the blocks in the block template, the post content has already been fully parsed including all the typical functions.
    • To avoid double parsing, it is then extracted out of the block template temporarily and replaced with placeholders.
    • The similar functions can then process the rest of the block template, without touching the post content again.
    • Once that has happened, the post content is re-injected in the correct places, keeping the overall template content intact while fixing the double parsing.
  • Having benchmarked this with a basic post in Twenty Twenty-Two, there is no notable performance benefit to this (neither faster nor slower, since replacing the content is a similar amount of logic), but the critical part here is fixing the double execution and the bugs that are resulting from it.

Trac ticket: https://core.trac.wordpress.org/ticket/55996

#9 @flixos90
18 months ago

  • Milestone changed from Awaiting Review to 6.2
  • Owner set to flixos90
  • Status changed from new to assigned

@adamsilverstein I've created a PR https://github.com/WordPress/wordpress-develop/pull/3549 for #55996, which will resolve this bug here as well. Please have a look.

@manfcarlo commented on PR #3549:


18 months ago
#10

I can see three major flaws with this implementation:

  1. If the `render_block` filter is used to change the string that was saved to the global variable, the string search will fail and the post content will not get replaced.
  2. If the same placeholder string was already present in the raw content, such as inside a Custom HTML block, it will get replaced unintentionally.
  3. If one of the shortcode callbacks called through do_shortcode corrupts the placeholder string, it will not get replaced and the post content will be missing.

There is an alternative implementation at https://github.com/WordPress/gutenberg/pull/44995 that avoids all of the above and is much cleaner.

@flixos90 commented on PR #3549:


18 months ago
#11

@carlomanf Thanks for sharing these. Some replies:

  1. That's a great catch. We can address that though, by moving the logic of storing in the global variable to the latest possible point, where no external code can interfere anymore.
  2. I think this really is a _theoretical_ concern. We definitely should use a placeholder string that is "unique" enough so that nobody will _randomly_ use a similar placeholder. Of course one can still choose to use exactly the same placeholder, but then you're intentionally breaking this feature, and it goes without saying that if you have code access and _want_ to break WordPress, you already can in millions of ways. Definitely open to choosing another "more unique" placeholder string if that's preferable.
  3. Realistically, under which circumstances could that possibly occur? The template content would need to have a shortcode, which is the first thing I question. Shortcodes are mostly a thing of the past, and while they are still used a ton in post content, I have yet to see or hear about any usage of shortcodes in a block template. More importantly though, even if a shortcode is used in a template, it would need to be a shortcode that wraps the post-content block, which I think is even far more unlikely. So this one I as well consider a theoretical concern.

To be clear, I'm not saying the last 2 things are impossible to happen, but they are extreme edge cases to the degree that makes them irrelevant (<0.1% of sites).

@flixos90 commented on PR #3549:


18 months ago
#12

@carlomanf I have pushed additional commits which address your points 1. and 2. While I don't think 2. is a significant concern, it's still trivial to address so I think it's worth doing.

The fix to point 1. led me to another neat code cleanup here, as I've now replaced the global with a simple local variable that cannot be tampered with outside of the intended usage (when parsing the core/post-content block).

#13 @flixos90
18 months ago

  • Keywords has-unit-tests added

https://github.com/WordPress/wordpress-develop/pull/3549/commits/ac75d2fc555de950835e23ce897f7d13ba84339b is a test that specifically covers this bug, as part of #55996. It confirms that we can use that PR to fix both of these tickets.

@manfcarlo commented on PR #3549:


18 months ago
#14

Points 2 and 3 that I raised can also be combined, so one of the shortcode callbacks could add and it would then get replaced. I wouldn't say it's necessarily an intentional attempt to break this feature, it might just be a coincidence that they choose the same string.

Why not use a string that is generated uniquely at runtime, such as with `uniqid`? Then, in case any corruption errors occur, they would likely be solved by refreshing the page.

Also, this solution does not stop double filtering when one post content is embedded inside another post content, which is a far more common occurrence than the other scenarios and is handled easily by https://github.com/WordPress/gutenberg/pull/44995. How feasible do you think it would be to modify this solution to handle it?

#15 @adamsilverstein
18 months ago

  • Milestone changed from 6.2 to Awaiting Review

In my local testing I can verify that:

I'm not familiar enough with the history of this code so I'm not sure which approach would be better. Removing the filters and replacing them does seem pretty foolproof, although adding tests on the PR would be more convincing.

This ticket was mentioned in PR #3560 on WordPress/wordpress-develop by @flixos90.


18 months ago
#16

  • This PR addresses all specific problems that causes the lazy-loading logic to not work correctly in block themes, covering both content images and featured images (i.e. post-content block and post-featured-image block).
  • In general, for either of them, one problem is that wp_filter_content_tags() runs over the entire template content after both the featured images and the post content images have already been processed once though. While the underlying problem for that is fixed in #3549, this PR fixes the loading="lazy" problem specifically in a decoupled manner, ensuring that by default the function always returns false when parsing the entire block template (via a new the_block_template context).
  • With that, there is still a remaining problem though that specifically the featured image in the post-featured-image block would still receive loading="lazy" by default. The reason for that is that, in a block theme, the relevant logic runs outside of the loop - which strictly speaking is a backward-compatibility break in the first place. A fix for the exact same problem is already present in the post-content block, so this PR simply adds the same fix to the post-featured-image block.
  • With these changes, the problems are addressed. Tests are included to verify all of the fixes work correctly.

Trac ticket: https://core.trac.wordpress.org/ticket/56930

#17 @flixos90
18 months ago

@adamsilverstein @manfcarlo @spacedmonkey I've looked into this bug further, also more specifically though https://github.com/WordPress/gutenberg/issues/41783 (which is caused by yet another problem, though it affects what is seen here too) and opened the above PR https://github.com/WordPress/wordpress-develop/pull/3560.

While #55996 remains the root cause for part of the problem here (and should still be fixed, e.g. via https://github.com/WordPress/wordpress-develop/pull/3549), the new PR really fixes the problems here more specifically. Tests to cover all changes fix the respective problem are included. Please have a look.

@flixos90 commented on PR #3549:


18 months ago
#18

Providing a quick update here, I've found another simpler fix for the lazy-loading problem from https://core.trac.wordpress.org/ticket/56930 specifically and opened #3560. Therefore I've removed one unit test from here which is now part of that PR. This PR here remains valid for fixing https://core.trac.wordpress.org/ticket/55996.

@flixos90 commented on PR #3549:


18 months ago
#19

@carlomanf

Points 2 and 3 that I raised can also be combined, so one of the shortcode callbacks could add and it would then get replaced. I wouldn't say it's necessarily an intentional attempt to break this feature, it might just be a coincidence that they choose the same string.

My previous comment remains, if you want to break this feature, you can. Let's be realistic here, what are the chances that there is a shortcode in an FSE template that wraps a post-content block and then adds some placeholder in there (without replacing it with anything, which would probably be a bug by itself!) that is exactly the same placeholder as WP core uses? Obviously WordPress operates at a massive scale and edge-cases occur, but what we're talking about here is extremely far fetched to the degree that I can't see it happening without intentionally wanting to break this.

Why not use a string that is generated uniquely at runtime, such as with `uniqid`? Then, in case any corruption errors occur, they would likely be solved by refreshing the page.

We can potentially combine this with the current approach. Using uniqid() by itself would arguably worse than the current approach, since now it attempts to find a placeholder that isn't anywhere in the content. A string created by uniqid() could also be in the template, while the current code makes a collision with the parsed post content impossible.

Also, this solution does not stop double filtering when one post content is embedded inside another post content, which is a far more likely occurrence than the other scenarios and is handled easily by WordPress/gutenberg#44995. How feasible do you think it would be to modify this solution to handle it?

What is a realistic use-case to include a post-content block within another post-content block? To my knowledge, that is not even possible since this is a template-specific block.

@flixos90 commented on PR #3560:


18 months ago
#20

The e2e test failing here is solely because I added one of the editor block files directly. In order to address that bit properly, I've opened https://github.com/WordPress/gutenberg/pull/45534 to bring the same change to Gutenberg.

#21 @adamsilverstein
18 months ago

  • Milestone changed from Awaiting Review to 6.2

@manfcarlo commented on PR #3549:


18 months ago
#22

Realistically, under which circumstances could that possibly occur? The template content would need to have a shortcode, which is the first thing I question. Shortcodes are mostly a thing of the past, and while they are still used a ton in post content, I have yet to see or hear about any usage of shortcodes in a block template. More importantly though, even if a shortcode is used in a template, it would need to be a shortcode that wraps the post-content block, which I think is even far more unlikely. So this one I as well consider a theoretical concern.

Imagine there is a shortcode that wraps some content, performs a word count on it, and prints out the word count appended to the raw content. Currently, if a post content block is added within the shortcode, the post content is included in the word count. After this patch is applied, the shortcode will begin to receive the placeholder and the post content will no longer be included in the word count.

I don't know of a particular shortcode that does the above or something like it, but I wouldn't underestimate the use of legacy features and I wouldn't at all be surprised if the bug gets reported at some point. For example, https://github.com/WordPress/gutenberg/issues/35676 is a separate but similar issue that shows how widely plugin/theme authors are using shortcodes together with blocks and discovering unexpected behaviour.

The issue with the hypothetical word count shortcode would actually be worse, because it would not be the unexpected behaviour of a new feature, but an unexpected change in behaviour after several release cycles given that shortcode support was added in 5.9.

The root cause of all of these bugs is that the block paradigm that was ostensibly intended to be a clean slate ended up receiving support for legacy features, some of which date back to the b2/cafelog days, and neither was ever designed to be compatible with the other. There is probably no way forward here that isn't going to break at least something, and quoting obviously fabricated statistics like "<0.1% of sites" is not going to be helpful, especially not after the bug reports inevitably roll in.

The main difference between https://github.com/WordPress/gutenberg/pull/44995 and this patch is as follows:

  • Both patches ensure that the filters never run more than once, and therefore solve any problems introduced by the double filtering, e.g. the lazy-load images.
  • This one does so by running the filters separately on different pieces of content and re-assembling them at the end.
  • The other patch does so by running the filters just once on the whole content, which might be better from a performance perspective.
  • Both patches are likely to break existing behaviour in different ways.
  • It will be practically impossible to quantify the scale of the breakage, especially before any patch is deployed.

@flixos90 commented on PR #3549:


18 months ago
#23

@carlomanf I agree with you in that there is a risk of potential breakage in this change. I do however still think that the risk for what you're pointing out to come up as a real-world bug is extremely low. It obviously does not make sense to argue about this because we can't know until it's out there. My take here specifically is that the chance of this causing a bug is so small that we can add it to core, basically "wait and see". If my assessment here shows to be wrong, and we get bug reports of this, I'll happily revert and then we have to look for alternatives. We also have beta period for that purpose (though even if this is in a stable release, we could revert it if it became a problem).

The implementation from https://github.com/WordPress/gutenberg/pull/44995 has other problems, specifically for the wp_filter_content_tags() function it introduces a backward compatibility break. The function is supposed to run specifically on the_content, as otherwise the lazy-loading logic will not apply correctly. It seems to me that the main difference between the two PRs is that this one keeps those functions running on the_content instead of the block template, while the other one runs them on the block template instead of the_content. The first is how WordPress has been behaving since forever basically, which is why I consider that a backward compatible approach.

@flixos90 commented on PR #3560:


17 months ago
#24

Update: The related Gutenberg PR has been merged (see https://github.com/WordPress/gutenberg/pull/45534). Once that update is in WordPress core, I can update this PR to have the e2e test pass.

This ticket was mentioned in PR #3702 on WordPress/wordpress-develop by @Mamaduka.


17 months ago
#25

Updates include the Post Featured Image block fix required for #3560.

Updates packages:

 - @wordpress/block-directory@3.15.12
 - @wordpress/block-library@7.14.12
 - @wordpress/customize-widgets@3.14.12
 - @wordpress/edit-post@6.14.12
 - @wordpress/edit-site@4.14.14
 - @wordpress/edit-widgets@4.14.12

Trac ticket: https://core.trac.wordpress.org/ticket/56930

cc @felixarntz

@Mamaduka commented on PR #3702:


17 months ago
#26

@felixarntz, I don't have commit access, so somebody should merge this PR.

@Mamaduka commented on PR #3702:


16 months ago
#27

Thanks for testing, @mukeshpanchal27!

There have been no new package releases targeting WP, so there is no need for an update. You can find more details on a package's versions page. Example https://www.npmjs.com/package/@wordpress/block-library?activeTab=versions

@mukesh27 commented on PR #3702:


16 months ago
#28

@Mamaduka @wordpress/block-library current version is 8.0.0 isn't it?

@Mamaduka commented on PR #3702:


16 months ago
#29

Correct, we do package releases every two weeks when the Gutenberg plugin is released. Those aren't intended for the core. See https://developer.wordpress.org/block-editor/contributors/code/release/#packages-releases-to-npm-and-wordpress-core-updates

The versions in this PR were cherry-picked for the core to help with #3560.

#30 @flixos90
15 months ago

In 55079:

Editor: Update packages to unblock lazy-loading issues.

Fixing the issue that featured images are always lazy-loaded in block themes required an upstream Gutenberg change https://github.com/WordPress/gutenberg/pull/45534. This changeset updates packages in order to unblock the remainder of the fix in trunk.

Props mamaduka.
See #56930.

@flixos90 commented on PR #3560:


15 months ago
#32

@adamsilverstein This PR is now ready for a full review, since https://core.trac.wordpress.org/changeset/55079 is now committed to unblock this.

I have updated this against latest trunk today and gave it another test, everything appears to be working well.

The latest version of the PR has a more comprehensive solution than before, also covering block template parts that run through this logic as well. An easy win here was to also not lazy-load images by default that are in the header template part, since those images are very likely above the fold anyway (they appear certainly before the content images that we have been handling so far). The most relevant changes for this are https://github.com/WordPress/wordpress-develop/pull/3560/files#diff-136b3429427993c3d47e44fe6c9a046635aa8d2c78542a90c8a172468addcb78R137 and https://github.com/WordPress/wordpress-develop/pull/3560/files#diff-4d28b6652f0dab208c19aa45e1cf09f3c967060d8cc77502bfcd473a3216b612R5441-R5444.

@flixos90 commented on PR #3538:


15 months ago
#33

@adamsilverstein I gave this another look, but I think #3560 is a bit of a cleaner solution as it relies on providing new contexts where they're missing today, and also doesn't disable lazy-loading completely for these contexts. The resulting behavior today would be the same, but as we are likely to further enhance the lazy-loading default heuristics later, I think it's better to handle all that in the wp_get_loading_attr_default() function.

@flixos90 commented on PR #3560:


15 months ago
#34

Too bad I didn't spot the issue with the template-part block before... Now that I edited it, I of course shouldn't have done that, instead we need to fix that bit in Gutenberg. I've opened https://github.com/WordPress/gutenberg/pull/47203 for that purpose. This is the only thing that makes the e2e test above fail.

While this PR has to wait for that to be merged and backported, in terms of code this is already good to review.

@flixos90 commented on PR #3560:


15 months ago
#35

Update: This PR is simply waiting for https://github.com/WordPress/gutenberg/pull/47203 to be backported to WordPress core, which will happen as part of the Gutenberg 15.1 RC backport.

The pull request can already be reviewed as is and should be fully functional and ready for commit as soon as the backport has happened. Please ignore the e2e test failure since that failure is purely due to this PR modifying the one file that should come from Gutenberg instead. This also means the changes in the template-part.php file can be pretty much ignored for code review, since they have already been merged in Gutenberg and will be made available in core soon.

#36 @antonvlasenko
15 months ago

Test Report

Patch tested: https://github.com/WordPress/wordpress-develop/pull/3560

Environment

  • OS: macOS 13.1
  • Web Server: Apache 2
  • PHP: 8.1.14
  • WordPress: 6.2-alpha-54642-src
  • Browser: Safari 16.2
  • Theme: Twenty Twenty-Three
  • No Active Plugins

The test steps are taken from https://github.com/WordPress/wordpress-develop/pull/3560 's desription.

Steps

  1. Activate the Twenty Twenty-Three theme.
  2. Create a post with 2 core/image blocks.
  3. On the same post, set a featured image. Save the post.
  4. Go to the Site Editor and edit the Header and the Footer template parts.
  5. Add 1 core/image block to each of the template part and save them.

Actual Results

  1. Open the post on FE.
  2. ✅ Out of 2 images that were added on step 2 (see Steps above), the first image does not have a loading="lazy" attribute, while the second does have it.
  3. Open the home page.
  4. ✅The featured image does not have a loading="lazy" attribute.
  5. ✅The image that was placed in the Header doesn't have a loading="lazy".
  6. ✅The image that was placed in the Footer does have a loading="lazy".

Notes

If you run npm run build:dev, the contents of the template-part.php file will be overwritten, reverting the fix introduced in https://github.com/WordPress/wordpress-develop/pull/3560. This needs to be addressed separately.


Last edited 15 months ago by antonvlasenko (previous) (diff)

#37 @flixos90
15 months ago

Thank you for giving this a detailed test @antonvlasenko!

Looks like it works as expected, though I saw you made some edits to your report and initially had one failure in there. Was that just because of what you highlighted in your note (npm run build:dev overriding the fix)?

That part is a known limitation right now; we will have to wait until #57471 has been committed (which includes https://github.com/WordPress/wordpress-develop/pull/3914/files#diff-136b3429427993c3d47e44fe6c9a046635aa8d2c78542a90c8a172468addcb78), then this will no longer be a problem.

#38 @antonvlasenko
15 months ago

Thank you for giving this a detailed test

You are welcome!

Was that just because of what you highlighted in your note (npm run build:dev overriding the fix)?

Yes, that's absolutely correct, @flixos90! I had to edit the test report when I realized why the fix "wasn't working."
Also, I've reviewed the code. It looks good to me, although I'm not an expert on rendering core/image blocks.

Last edited 15 months ago by antonvlasenko (previous) (diff)

#39 @ironprogrammer
15 months ago

  • Keywords has-testing-info added

Thank you for the testing information @antonvlasenko 🙌🏻 Adding corresponding keyword.

@flixos90 commented on PR #3560:


14 months ago
#40

@adamsilverstein @mukeshpanchal27 @hellofromtonya This is now ready for a full review. Since the Gutenberg package updates have been backported, the remaining conflict is now resolved.

It would be great if we could land this in time for the 6.2 Beta 2, as it would benefit from further testing on real websites using the Beta.

@mukesh27 commented on PR #3560:


14 months ago
#41

@felixarntz The code changes appear to be correct to me, and I tested all of the test scenarios listed in the description, and they all worked properly. Great work!

@manfcarlo commented on PR #3549:


14 months ago
#42

Also, this solution does not stop double filtering when one post content is embedded inside another post content, which is a far more likely occurrence than the other scenarios and is handled easily by WordPress/gutenberg#44995. How feasible do you think it would be to modify this solution to handle it?

What is a realistic use-case to include a post-content block within another post-content block? To my knowledge, that is not even possible since this is a template-specific block.

It is possible, encouraged, and mentioned in this video at 5–6 minutes, 18 minutes and 32 minutes. It is also mentioned in this article and this article.

Handling post content within post content is essential for this issue to be solved, and currently, this patch does nothing to handle it.

@flixos90 commented on PR #3560:


14 months ago
#43

@mukeshpanchal27 @costdev Thanks for the feedback and testing! I think I have addressed it all, please take a look.

@robinwpdeveloper commented on PR #3560:


14 months ago
#44

Some checks are failing!
Do we need to re-run or is it legit issue?
All of errors saying:
Error: Command failed: docker-compose run php ./vendor/bin/phpunit --verbose -c phpunit.xml.dist

#45 @shahidul95
14 months ago

Test Report

This report validates that the indicated patch addresses the issue.

Patch tested: https://github.com/WordPress/wordpress-develop/pull/3560

Environment

  • OS: macOS 12
  • Web Server: Nginx
  • PHP: 7.4.30
  • WordPress: 6.2-beta1-55292-src
  • Browser: Chrome 110.0.5481.77
  • Theme: Twenty Twenty-Three
  • Active Plugins:
    • No Plugins activated

Actual Results

  • ✅ Issue resolved with patch.
  • ✅ Header image doesn't have loading="lazy"
  • ✅ Featured image doesn't have loading="lazy"
  • ✅ First image (if no featured image added) doesn't have loading="lazy"
  • ✅ Footer image has loading="lazy"
  • ✅ Second image has loading="lazy"

Additional Notes

Steps followed:

  • Activate the Twenty Twenty-Three theme.
  • Create a post with 2 core/image blocks.
  • On the same post, set a featured image. Save the post.
  • Go to the Site Editor and edit the Header and the Footer template parts.
  • Add 1 core/image block to each of the template part and save them.

Supplemental Artifacts

After applying patch:

#46 @reduanmasud
14 months ago

Test Report

This report validates that the indicated patch addresses the issue.

Patch tested: https://github.com/WordPress/wordpress-develop/pull/3560

Environment

  • OS: macOS 11
  • Web Server: Nginx
  • PHP: 7.4.30
  • WordPress: 6.2-beta1-55292-src
  • Browser: Chrome 110.0.5481.77
  • Theme: Twenty Twenty-Two
  • Active Plugins:
    • No Plugins activated

Actual Results

  • ✅ Issue resolved with patch.
  • ✅ Header image without loading="lazy"
  • ✅ Featured image without loading="lazy"
  • ✅ When post has no featured image - first image without loading="lazy"
  • ✅ Second image with loading="lazy"
  • ✅ Footer image with loading="lazy"

Additional Notes

Followed steps provided above.

  • Activate the Twenty Twenty-Two theme.
  • Create a post with 2 core/image blocks.
  • On the same post, set a featured image. Save the post.
  • Go to the Site Editor and edit the Header and the Footer template parts.
  • Add 1 core/image block to each of the template part and save them.

Supplemental Artifacts

After applying patch:

#47 @mukesh27
14 months ago

  • Keywords commit added

Thanks @flixos90 for the ticket and PR.

PR 3560 got enough approval for a merge, so adding commit for consideration

@flixos90 commented on PR #3560:


14 months ago
#48

@robinwpdeveloper @costdev Test failures have been fixed.

@flixos90 commented on PR #3538:


14 months ago
#49

Closing in favor of #3560.

#50 @flixos90
14 months ago

  • Resolution set to fixed
  • Status changed from assigned to closed

In 55318:

Media: Enhance logic to determine LCP image in block themes and avoid lazy-loading it.

[52065] originally introduced the logic to guess the LCP image based on certain heuristics and to not lazy-load that image. However, with the introduction of block themes, that logic was not functioning correctly, resulting in all featured images to be lazy-loaded, regardless of whether it was the LCP image or not.

Together with an update to the core/post-featured-image block included in [55079], this changeset fixes the logic to correctly handle featured images in block themes as well.

Additionally, in combination with an update to the core/template-part block from [55246], this changeset includes an enhancement which uses the benefits of block template parts to avoid lazy-loading images in the header block template part, making the lazy-loading heuristics even more accurate for sites using a block theme.

Props flixos90, adamsilverstein, mamaduka, antonvlasenko, shahidul95, reduanmasud, costdev, mukesh27, ironprogrammer, manfcarlo, robinwpdeveloper, spacedmonkey.
Fixes #56930.

#52 @milana_cap
14 months ago

  • Keywords add-to-field-guide added

#53 @flixos90
14 months ago

I did some quick performance benchmarking for this feature.

While we already have a more general Web Vitals comparison for WordPress 6.2 and 6.1, those tests were made without a post that contains an image, which is specifically something that this fix helps with (for block themes only).

Doing a similar test with a block theme page with an image shows a notable ~19% LCP improvement in 6.2 (for content without an image it was only ~14%), and focusing on client-side performance specifically, the combined metric LCP - TTFB is improved by ~13% in 6.2 (for content without an image it was only ~3%).

So it is fair to say that this is a major win for client-side performance for any block theme that uses a hero image.

Here is the data: https://docs.google.com/spreadsheets/d/1n2fTB8DFGHpdrqIl9FP_Cjb7umPmidadjJChvbBRbXI/edit

#54 @flixos90
11 months ago

In 55847:

Media: Conditionally skip lazy-loading on images before the loop to improve LCP performance.

When the logic to exclude images that likely appear above the fold from being lazy-loaded was introduced in WordPress 5.9, initially only images that appear within the main query loop were being considered. However, there is a good chance that images above the fold are rendered before the loop starts, for example in the header template part.

It is particularly common for a theme to display the featured image for a single post in the header. Based on HTTP Archive data from February 2023, the majority of LCP images that are still being lazy-loaded on WordPress sites use the wp-post-image class, i.e. are featured images.

This changeset enhances the logic in wp_get_loading_attr_default() to not lazy-load images that appear within or after the header template part and before the query loop, using a new WP_Query::$before_loop property.

For block themes, this was for the most part already addressed in [55318], however this enhancement implements the solution in a more generally applicable way that brings the improvement to classic themes as well.

Props thekt12, flixos90, spacedmonkey, costdev, zunaid321, mukesh27.
Fixes #58211.
See #53675, #56930.

Note: See TracTickets for help on using tickets.