Problem/Motivation
Lazy-loading of page elements, especially images, is a powerful way to increase perceived performance, reduce time-to-first-render, and reduce user friction on the web. In the past, most lazy-loading solutions were non-native, and required fairly complex setup and integration. In the Drupal contributed module space, there is: drupal.org/project/lazy which is built on the lazysizes javascript library. Drupal Core also supports BigPipe rendering for personalized content, however this technique requires some configuration and technical know-how, and, in classic Drupal fashion, perhaps solved the harder problem before addressing the easier one.
The html 'loading' attribute was first proposed as a new standard by the Chrome platform team, and was eventually merged into the WhatWG Living HTML specification. As is best practice with other lazy-load implementations, the 'loading' attribute is implemented by most browsers to avoid 'reflowing' content (aka 'layout shifting' or 'content shifting' i.e: causing the page to jump around as elements load in).
The 'loading' attribute is now supported for 74% of all users (full: 70.6%, partial: 3.4%) according to caniuse.com, including the most recent versions of Edge, Firefox, Chrome, etc. There is preliminary support for webkit, although support for Safari and several of the mobile browsers is still experimental, however the attribute is simply ignored and the assets rendered as normal in this case, making a completely clean and non-destructive fallback.
Proposed resolution
TBD
Remaining tasks
TBD
User interface changes
TBD
Release notes snippet
Adds load=lazy to img tags by default so as to increase perceived performance, reduce time-to-first-render and reduce user friction on the web.
| Comment | File | Size | Author |
|---|---|---|---|
| #64 | issue-3167034-switch.patch | 3.57 KB | caohan@ciandt.com |
| #30 | lazy-loading—display-mockup.png | 222.67 KB | chrisfree |
Issue fork drupal-3167034
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
- 3167034-lazy-load-images
changes, plain diff MR !6
- test
changes, plain diff MR !8
Comments
Comment #3
edysmpHere is the first attempt on getting the loading attribute added to the majority of the images rendered by Drupal Core.
Check the commit here.
Comment #4
heddnThis looks good as a start. It doesn't account for base theme logo images. But it should cover most images. Still needs tests too.
Comment #5
heddnComment #6
edysmpWorking on tests.
Comment #8
edysmpAdded a simple test coverage for this new feature.
IMHO I think we shouldn't implement lazy loading for the themes logos since they usually load at first neither add a test coverage that prove that.
Comment #9
andypostLooks ready for review, test added
Comment #10
sokru commentedWorks nicely on regular media fields, but is it out of scope to add also to core ckeditor?
Maybe we should document why width/height is checked?
Following is taken from https://web.dev/native-lazy-loading/ Without dimensions specified, layout shifts can occur, which are more noticeable on pages that take some time to load.
Comment #13
edysmp@sokru Yep ckeditor support is something we want. Checking that.
Comment #14
edysmpAdded ckeditor support.
Comment #15
edysmpComment #16
edysmpFixing tests.
Comment #17
edysmpUpdated ImageDimensions test asserts
Comment #18
edysmpFixing tests.
Comment #19
edysmpUpdate EditorPrivateFileReferenceFilterTest test to use a real image to get getimagesize function happy.
Comment #20
heddnComments posted on MR.
Comment #21
edysmp@heddn Thanks for checking it.
Working on it.
Comment #22
edysmpWorked on #20.
Comment #23
hestenetPassing on a few comments from folks on the technical team at Google who don't yet have accounts here on Drupal.org:
And then unrelatedly:
Comment #24
heddnI feel like adding loading=lazy on iframe in _this_ issue would stretch the scope a little. Additionally, drupal core only does it in a single location, OEmbedFormatter field formatter. We might find some benefit from adding loading=lazy to it, but let's do that in a follow-up.
NW for the rest of the items brought up by Google.
Comment #25
edysmpAddressing #23 and #24 feedback.
Comment #26
andrewmacpherson commentedIf the
loading="lazy"is hard-coded in the theme function, a site builder gets no say in things. How do we setloading="auto"orloading="eager"?At the least, shouldn't lazy loading be a configurable option on image field formatters and the CKEditor tool?
Comment #27
heddnre #26: it isn't hard-coded into the theme function. If someone passes loading=auto or loading=eager in, this will not overwrite it. It only sets the value by default. Or do I mis-understand the question?
Comment #28
gaëlgHi there, I just got aware of this initiative. I don't have much time to read this whole thread right now, but I have to mention that 2 months ago I started a little module that tries to do that: https://www.drupal.org/project/native_lazy_loading
Anyway, I'll have a closer look at this issue later, as it looks like there's some sort of effort duplication.
Comment #29
gaëlgThank you all for your work on this useful initiative. I added some comments on git.drupalcode.org.
Some more general thoughts I had by comparing the core patch and the humble contrib module I implemented:
Comment #30
chrisfree commentedI have to agree with @andrewmacpherson here (#26). I think it is important to allow for some images to be configured when
loading="eager"is appropriate. I recently implemented a primitive version of this on Chromatic's website wherein we used an image style's ID to conditionally setloading="eager"in certain cases. Here is some sample code showing we pulled this off:This obviously isn't what I would suggest for core, but it does demonstrate how one might conditionally add "eager" to their rendered markup.
Instead, I think it might make sense for core to expose this on image field formatter as an optional configuration. Perhaps something like this?
The options would map to the supported values:
auto, lazy, eager,lazybeing the default. Rendered markup would respond accordingly, based on this setting. I could also see this additional display field/UI being gated behind a higher level setting if its inclusion by default is considered too heavy handed.Tangentially related, I also wrote a simple input filter for ensuring hard-coded images in legacy content include native lazy loading attributes. Might be useful for others watching this issue.
Comment #31
heddnCan someone report back if the approach used here wouldn't let anyone to set eager, auto, etc? We can always add more bells and whistles to this feature set. But I'd really like us to not let the perfect be the enemy of the good enough. Meaning, would the patch here break any sites as-is?
Comment #32
heddnRe #29.1: no, it does this on all images, not just ckeditor. See https://git.drupalcode.org/project/drupal/-/merge_requests/6/diffs#af11a...
29.2/29.3: Do we need explicit test coverage for these? They are covered by the current solution. But both of these use the same theme_image under-neath everything, so adding an additional test seems like not necessary.
Comment #33
gaëlgClarification about #29.1: it wasn't clear, but what I meant is: width and height attributes are indeed added in preprocess() if width and height variables are defined. But if the variables are not defined, there's no code to try to get them for image fields, unlike editor images where
$image->getWidth()get called.Well, I did not check: maybe it's unneeded because core always gives width and height variables for image fields. I'll give this patch a real-life test with different scenarios to ensure it works well (responsive or not, styled or not, html editor or image field).
About #30: "I could also see this additional display field/UI being gated behind a higher level setting if its inclusion by default is considered too heavy handed." Yes, I'm wondering if this UI would be necessary enough for it to be displayed by default on all websites. An opt-in might be better. In real life, is it usual that loading=lazy is inappropriate? I can't tell.
Anyway, if this patch can "break" some websites displays (can it?), we must ensure site builders can disable it.
By the way and for the record, compatibility with other image-related contrib modules like webp, svg_image and config_default_image has to be thought, but I guess it will be contrib follow-up issues.
Comment #34
chrisfree commentedFrom Google's web.dev page on implementing native lazy loading:
Drupal image presets and/or display formatters have no way of knowing which images will be rendered above the fold. While that same documentation page describes the impact of setting
loading=lazyon images within the viewport as "minimal", it is still not zero. My vote would be, to allow those interested, to finely tune theloadingvalue that Drupal renders.Comment #35
heddnGreat feedback so far. However, I feel like we are getting too excited about this feature and moving to the next logical conclusion. Which is adding a UI to make this configurable. But it doesn't mean we shouldn't let the perfect stop us from landing something less then perfect.
See #3173180: Add UI for 'loading' html attribute to images for a follow-up.
With that in mind, is there any actionable feedback for setting loading=lazy by default? But letting it be overridden?
Comment #36
heddnFixing issue relationship.
Comment #37
edysmpThank you all for your feedback!
Added test that proves loading attribute with lazy default value can be overriden.
So far we don't have support for `Responsive image` module. IMHO it could be done in a follow-up issue.
Comment #38
heddnAdded another follow-up, #3173231: Enable lazy-load by default for responsive images.
and re #33
Getting the image dimensions is scope addition. If core doesn't currently have it, then we can discuss trying to get it. Again in a follow-up.
Part of my push to get this to RTBC is that if we can get this to land in the next few days, we'll have it enabled by default in 9.1. Which is a really big win for the world. Fewer CPU cycles on internetworks all around the world.
Comment #39
ressaUpdated the statistics and added a link to the 'loading' attribute page at caniuse.com in Issue Summary.
Comment #40
heddnI've not used this PR/MR approach before on d.o, but I think all the feedback that we can do in this issue is now addressed. And several follow-ups added. Last things, I think we need a minimal CR and a one line sentence for the 9.1 release notes.
Comment #41
catchJust reviewed the PR again and it looks fine to me, great that this is such a straighforward change.
Comment #42
edysmpAdded release notes snippet and change record.
Comment #43
heddnAll feedback address. We've got a release notes snippet and a CR. Shall we try out RTBC?
Comment #44
andypostAdded 2 suggestions
Comment #45
edysmpAddressed #44. ty.
Comment #46
heddnI think RTBC again?
Comment #47
gaëlgRe #38: I manually tested the latest patch in "real life" with and without image style, in image fields and in html editor. The loading=lazy attribute is correctly set, and width and height attributes are correct too! I did not test responsive images as they will be handled in #3173231: Enable lazy-load by default for responsive images.
RTBC+1 :)
Comment #48
catchThe PR can't be merged and needs a rebase. I haven't actually tried out merging a PR on gitlab yet but there's always a first time.
Tagging for 9.1.0 highlights since this is a nice performance improvement that should benefit most sites.
Comment #49
edysmpFixed wording and updated branch with 9.1.x changes.
Comment #51
catchDid a cli merge of the PR (was one commit behind), and pushed to 9.1.x, thanks!
Comment #52
catchComment #53
krzysztof domańskiComment #54
xjmSo, generally, only disruptive changes are included in the release notes, and the release note should describe the disruption. (What it was like before -- what it's like now -- who is affected -- how they can fix it.) Will this issue cause any such disruptions? If so, we need to fix the release note to describe them.
"9.1.0 highlights" is what gets this mentioned on the the frontpage post as a performance improvement, so that's covered already. :)
Comment #55
heddnThis should just be in the highlights, not the release notes.
Comment #56
xjmThanks @heddn; @catch and I agree as well.
Comment #59
pivica commentedWe noticed while checking this issue and testing with responsive images that responsive images are missing width and height attribute which is bad because:
- browser can not determine the needed layout for images in advance and then when images are loaded unnecessary additional page layout and repainting will happen,
- MR in this issue is disabling lazy loading for images where width and height attributes are missing.
I've created a simple core patch in #3192234: Apply width and height attributes to allow responsive image tag use loading="lazy" which is adding width and height attribute for this case and tests are showing that this works great and eliminates both problems. Can we get somebody from this issue to maybe check the proposed patch in 3192234 and give some thoughts on it, thank you.
Comment #60
gaëlg@pivica, responsive images are out of scope of this issue, it has been discussed in #3173231: Enable lazy-load by default for responsive images.
Comment #61
digitaldonkey commentedAny best practices for screenshot tools like puppeteer / backstopjs?
If there is an easy way to disable we might document it here. Or somewhere less obvious for me.
Comment #62
heddnAnother lazy load issue, for those interested: #3212351: Lazy load OEmbed formatter
Comment #63
akalata commentedThanks chrisfree for #30, how to get around forced lazy loading (Chrome is only showing ~25% of my site's images) until #3173180: Add UI for 'loading' html attribute to images is finished.
Comment #64
caohan@ciandt.com commentedHi, All,
I added a switch for this.
Because our sites use css to control image style. if add [height,width] to all File_Entity(image), it will affect site image layout.(attribute - height, width is the highest priority)
Comment #65
xjmHi @caohan@ciandt.com, I would suggest opening a new issue for your proposal.
Comment #66
heddnre #64/65: and cross post the link here to the new issue here. The addition of size to an image shouldn't take precedent over CSS, but I'm curious how things are setup on your site.
Comment #67
graber commentedAhh, sorry, wrong issue.
Comment #68
wim leersThis introduced a regression in
EditorFileReference: #3336446-3: EditorFileReference should compute a px <img height> if a % <img width> is specified, even though % <img width> is not allowed in HTML5.