Thank you, everyone for joining my lazy load talk, which is coincidentally at Web Directions: lazy load.

Henri and I did a small sort of promo video for this and in that he called this "the title track" of Web Directions: lazy load, and that was cool.

Made me a little bit nervous, but I appreciate you taking the time to learn with me, and I hope you're enjoying the conference so far.

I'm sure it's been impressive - John and the Web Directions crew have always put on a really great conference when I've had the opportunity to attend them in the past.

My hope is that we get to do this in person soon though, but I'm grateful for the Web Directions team for making these spaces available until we get that opportunity.

In this talk, though, we're going to look at native browser lazy loading.

So, whether it's an image or an iframe (both are supported) to lazy load, you add 'loading ="lazy"' to the element and you have enabled native lazy loading.

So thank you.

And I hope you've enjoyed my lazy loading talk and that you enjoy the rest of the conference.

If you have any questions....

Okay no, there is more to this talk - I hope no one was panicking?

And there's more to native lazy loading than just that single property, and I think it can be deceptive in that sense.

In this talk now, along with looking at how we use lazy loading, we're going to explore why we needed lazy loading, what we've had until native lazy loading was introduced, and why we should use it in some situations that you should be aware of.

Native lazy loading first appeared in Chrome 77, which is about a year and a half ago.

Edge added support through becoming a Chromium browser.

The spec actually merged after those two occasions with the WHATWG HTML standard adding that in February of 2020.

Browsers like Safari and Firefox have added support, but in different ways.

Safari has it behind a feature flag, and Firefox 75 added lazy loading support, but it is only for images, not for the iframes that we saw earlier.

If you are interested in adding it in Safari, you select the "Develop" menu, "Experimental Features", and "lazy image loading" will be there.

So keeping those browsers in mind with that run through lazy loading isn't quite ubiquitous yet.

We're at about 73% global availability.

We can start using it though with some reasonable confidence.

So what's the reason for needing lazy loading in the first place?

If we were to say, look at HTTP archive data, we see that the median number of image requests, (this is per page) is 26 on desktop and 24 on mobile.

Now I have a little bit of a browse through their data, and that's almost down 50% from earlier data, but this still makes images the highest requested item when visiting a site and those requests happen early, no matter where in the page that image is actually appearing.

The image's weight, as I'm sure a lot of us aware, also has an impact.

Looking again to HTTP archive, we see that mobile pages transfer 902 kilobits of data and just slightly more on desktop at 948 kilobytes, but almost a meg(abyte).

And these values are up 25% for mobile, which depending on the device and the connection is a big jump.

So between the browsers, the device and the connection, all images are expected to be fetched, even if the user never makes it down to that section of the page.

Thus to improve performance of the site, we developers have looked at ways of delaying that request of the images until users truly needed them, needed to see them.

The idea of lazy loading was born out of that need.

The reason a native solution was created is because of the drawbacks of the approaches we had access to.

And this is in no means saying that they're bad.

They worked and have worked for a while, but when a feature becomes native to the ecosystem, it can really help to smooth out some of those bumpy areas of the solutions we've come up with.

I find this really a great part of the open web.

A problem exists, developers find tricks or hacks and solutions to those problems.

They become popular enough that browser vendors and standard writers notice that popularity and native solutions become a part of the browsers themselves.

But if we consider one of the first approaches through scroll detection with an event listener the event handlers could become pretty memory intensive and the balance was tough to get right.

It was entirely possible that an image would just get missed.

And with the whole idea of making the page more performant, this had the side effects and the downsides of that.

Intersection observance solved the performance issue, right.

It was more baked into the browser, but you could still hit issues with browser support.

That meant some of the users used that scroll detected version as a polyfill, doubling up on code, or if JavaScript was disabled, potentially not having images work at all.

So while performant, it still wasn't quite reaching that goal that we have.

We could have - in those scenarios where JavaScript was disabled - images using data attributes and possibly a NoScript version of the images like a backup, but we're increasing the maintenance costs.

You're almost doing everything two or three times.

Native lazy loading looks to solve the drawbacks of these JavaScript approaches and the first goal is no JavaScript being required.

We're saving the weight and processing time of that JavaScript.

That means that there isn't a need for a NoScript version.

Although we are going to have a look at a polyfill, but hopefully we won't need that for long.

Reliable preloading was another goal here.

As the user scrolled, the browser's going to handle and make sure that image does in fact show, and we can quite easily decide whether we do or don't want to lazy load.

So let's take a look at the 'loading' attribute and see what the accepted values are.

The definition is 'loading' for the property, with the accepted values of 'lazy' or 'eager'.

Eager, which is the default missing or invalid value.

This is similar to not having the lazy load prop at all, even if you are setting it to eager.

The browser is going to attempt to fit the image as soon as possible.

Lazy though, this is the native implementation of lazy loading with this on an iframe or an image, the browser will not commence loading until it has reached a certain point in the viewport.

But I guess that raises a good question right?

What is the point that the browser decides to show the image?

And to give maybe an annoying answer or a classic answer: "It depends".

Chrome has different values that depend on multiple factors.

That could be the devices 'form factor' - so if it's a mobile device, tablet, desktop; the network speed - so whether they're on a good connection or a flaky connection; if the user has 'light mode' enabled; and even their connection status - if they're offline, if it does a really long sort of value then it will try and load something.

The values though, ranged from about 1,250 pixels on a good, decent 4G connection down to 8,000 pixels on a slower 2G connection, right?

So image 1, 2 and 3 in the diagram - if you're on a slow 2G connection - are all going to try and load because they're within that band.

Those values are set by the browser, and at least as of this talk, you can't override them.

I've read some discussions around the idea of customization being available to us developers in later iterations of the spec.

There's no word on when that is, but I'm kind of okay with that for now.

I feel like the browser vendors will make informed decisions here based on data and metrics that they have, but we'll see what happens.

So we know that adding 'loading="lazy"' to the image and it lazy loads, and the factors that affect when that lazy loads.

Let's take a look at an example.

So here we have a page with images and the network panel open.

As I'm scrolling through the page, we are going to see more and more of those images start to load in.

They've not all been loaded and fetched at once, like they would without lazy load.

That's saving all of that data from the user side from being loaded.

If we simulate our connection down to a slow 3G, we start to see the loading triggers much earlier, because the browser knows that it's going to take considerably more time to gather all of those images.

All of this without extra JavaScript libraries.

So it's a bit of a double win if we think of it another way - you have lazy loading on the images, we're not getting that data - and less JavaScript is being sent to users, particularly important on a slower connection.

There are scenarios where lazy loading won't work though.

One is if the browser doesn't support the property, doesn't know what to do with it, right?

So it's going to ignore it.

We saw which browsers do it at the start.

And we'll look at some options like the polyfill.

The other might seem surprising when we think back to the goals we were talking about before.

If JavaScript is turned off, lazy loading doesn't happen, but I thought the whole idea was it didn't need JavaScript?

And I was initially confused by that as well.

But here we can see the demo of the same page that we saw before: the network panel is again open and JavaScript is now disabled.

Images are loading how they would if we didn't have the property, or if we'd used 'eager'.

We still see the images, though we aren't needing a NoScript version, so we are saving on that extra maintenance.

Like I said, I was curious as to why that was the case, and the reason the browsers are doing this is tracking prevention.

If a user disables JavaScript, the server could essentially use each of those requests as a user was going down the page as a way of tracking and understanding where they were on the page based by the network requests that are happening.

I wouldn't have thought of that, but it's nice that the browser vendors did at least.

The other scenario that I mentioned when the loading property isn't supported - there's a polyfill that we're going to have a look at - but there was consideration for sending a support param with the request.

This is similar to WebP or AVIF images, and that determines whether or not it's supported, but that was decided against, as the browser will eventually all support loading.

The polyfill is written by Maximillian Franzke (which I've hopefully pronounced correctly) and is called: "loading-attribute-polyfill".

Because of the way images in JavaScript are loaded, this does require changing the HTML set up of our page.

JavaScript doesn't have a way of capturing the images before the browser has gone off to request them.

Here's an example straight from their docs.

NoScript is used to make sure that image will still display with JavaScript disabled, it's got the image then inside of it.

The image has 'loading="lazy"' for browsers that do support the loading attribute and have that native lazy loading.

The only problem is that the JavaScript sort of has to parse and execute and take the content out of the NoScript element first.

Browsers that don't support lazy loading simply ignore the attribute there - they pretend it doesn't exist.

Now there is a JavaScript check that can determine whether it's supported in the browser.

So if 'loading' is in the HTML element prototype, we know that yep, loading is supported.

If not, then we have to do something.

That's where sort of the polyfill takes over at this point and uses what we're familiar with now to enable lazy loading on browsers.

For me, this is, would have been nice to have the support set up for browsers that support native lazy loading to let the server know we could have just returned the image with loading equals lazy and been done - not having all that extra setup and JavaScript, even if it isn't or is supported.

But hopefully it won't be long until browsers are at a point where it won't matter anymore.

The route you choose though, is really going to be defined by your users.

What are their needs?

What browsers are you supporting?

Can you reasonably ignore browsers that don't support lazy loading and just send the images?

Or are you going to need to have that JavaScript fall back in place?

Let's recap the use of lazy loading, because I was joking around a little bit at the start, and I want to make sure that we are clear.

For an image, the setup looks like this: 'Image source is donut'.

Alt tag will likely be there and 'loading="lazy"'.

Add the property, and you're good to go.

When inside a picture element, only the image requires the 'loading' attribute.

It doesn't need to be added to 'picture', it doesn't need to be added to the 'source' elements, which is nice - saves us some time.

For iframes, it's similar to an image element.

The prop is added to the element and we're done.

Having worked a lot with ad tech - as in ingesting ad tech - I can tell you this will be amazing for developers working in that space, and for those of us that even see ads.

Ads load just so much stuff and putting that off until we need it?

That is going to be a real win for users.

Remembering though that iframe support isn't equal.

Firefox doesn't have support for iframes yet, whereas Chrome does.

So check in a site like: "Can I use" before you do that to again, know what your users are gonna need.

What are some other potential 'gotchas' though and guidelines when working with lazy loading?

It's recommended not to add loading to images that are above the fold.

Now I know personally that it can be difficult to know if an image is above the fold or not above the fold.

There's a lot of different devices, but a hero image is a good example in all likelihood that is going to be above the fold.

And we don't want to add it to that because there's extra processing time where the browser has some notice that it is visible already and then go and fetch it.

We want that to happen as soon as possible.

A footer is likely something that is definitely not going to be above the fold.

So images down there, lazy load them and you'll be fine.

The second gotcha is that Chrome is not going to lazy load hidden iframes.

These are used for sneaky tracking or something like that, but it's worth noting.

If the iframe isn't visible, this could be display:none or any of those sort of CSS variations, then it won't be lazy loaded, but requested eagerly.

Lazy load does not affect CSS background images.

There isn't a way in this setup to lazy load a CSS-defined background image.

If you want the image to be lazy loaded, then you need to use the image element.

You will want to be considerate of 'Layout Shift', or 'Cumulative Layout Shift' if you're thinking from a web vitals sense.

So what's that?

Well I also (shameless plug) did a talk covering aspect ratio at Web Directions: hover.

So if you have access to the videos, I go into this a lot more detail in that talk, but the gist of it is this: if you lazy load an image that doesn't have width or height, it can cause the content of your page to jump.

I'm sure many of us have had this happen - when you're going to do something the image jumps and you've now clicked the wrong link or clicked something that you didn't mean to.

In this case, we're now permanently subscribed to my cat newsletter.

If the image doesn't have dimensions, the browser doesn't know how much space to allocate.

It is still going to lazy load - so we are getting that benefit - but you're going to cause other issues.

So it's good to have both and be considerate of both.

Adding width and height to the image is the way that we can avoid that scenario.

So that wraps up my talk on native lazy loading (and this is actually the end of my talk this time!) I hope I was able to provide you with some new information about lazy loading that will help improve the performance of your projects that you're working on, or reduce the complexity of the lazy load implementation that you have now.

Once browsers support lazy loading across the board, I think we'll one day look back similar to what we do now at, say, the number of images we used to use for border radius and have a bit of a sensible chuckle at how, you know, it used to be so much harder to do lazy loading.

And now it's a single property on elements.

That will be a nice place to end up at.

If you have any follow-up questions, please don't hesitate to reach out to me on Twitter, or I might be in the chat there depending on time zones.

My username though on Twitter is AntonJB - happy to provide any further information where possible.

Otherwise, I hope that the next time I'm presenting it will be with people in the crowd so that I'm not talking to myself in the lounge room with a judgemental cat looking at me!

Have a great rest of the Web Directions: lazy load conference.

Stay safe and healthy, and hopefully I'll see you soon.

Cheers.

Code demo of how to enable native lazy loading on an image or iframe element. Arrows are overlaid of both sections showing where the 'loading="lazy"' property value is added to the sequence

<img src="doughnut.jpg" alt="..." loading="lazy"/>
<iframe src"..." loading="lazy">

Image of Anton

Thank you

antonjb

Timeline of icons representing different browsers tracing when native lazy loading appeared and why. In Chrome in Sept 2019, in Edge in Jan 2020, merging with WHATWG HTML standard addition in Feb 2020, in Safari in Mar 2020, and Firefox in Apr 2020. Safari has a flag overlaid to demonstrate the feature is behind a flag, and Firefox has a black asterisk overlaid to demonstrate that the feature is only for images and doesn't include iframes.

https://caniuse.com/loading-lazy-attr

Image of the Safari logo with a flag overlaid

Develop
> Experimental Features
> Lazy Image Loading

Image of where to add Lazy Loading in Safari, showing the 'Develop' menu selected and then 'Experimental Features' selected, and 'Lazy Image Loading' selected from that sub-list

73 %

global availability

https://caniuse.com/loading-lazy-attr

Median Image Requests

per page

26
desktop

24
mobile

https://httparchive.org/reports/state-of-images

Median Image Transfer Size

per page

948kb
desktop

902kb
mobile

https://httparchive.org/reports/state-of-images

Image of a group of abstract arrows pointing toward a murky abyss, representing browsers, devices, and network all trying to fetch images at once

addEventListener('scroll', loadImages);

X Requires JavaScript
X Poor Performance
X Potentially unreliable

const imgObserver = new IntersectionObserver(loadImages);
imgObserver.observe(imageToLoad);

X Requires JavaScript
X Browser Support

<img data-src="doughnut.jpg" alt="..." />
<noscript>
<img src="doughnut.jpg" alt="..." />
</noscript>

X Maintenance

*

No JavaScript
No NoScript Element
Reliable preloading
Decide whether to lazy load

loading:   eager | lazy

  property              value

loading:   eager | lazy

  property              value

Image of a large arrow pointing to the 'eager' value in the code with the caption: "default, missing and invalid value"

loading:   eager | lazy

  property              value

Image of a large arrow pointing to the 'lazy' value in the code with the caption: "native lazy"

Image of a graph with a Chrome logo overlaid and the following text outlining a range of factors which may impact when the browser may implement lazy loading:

FORM FACTOR
NETWORK SPEED
LITE MODE
CONNECTION STATUS

Image of a graph representing pixel sizes overlaid with a stack of three colored boxes of different sizes overlaid with different viewport sizes and network capabilities, representing the range of values within which the browser may currently try to load lazy images

1 - 4G - 1,250PX
2 - 3G - 2,500PX
3 - 2G - 8,000PX

Animated demo of a page lazy loading, with the network panel view open to the right of the images. As the page is scrolled, more images load in and appear at the bottom of the scroll and their corresponding selection line appears in the network panel one by one as each image loads, showing how they are not all being fetched at once as they would without lazy loading applied

A large yellow arrow is overlaid pointing from one image of a donut in the scroll to its corresponding selection line in the network panel

Side by side images of rows of image file names - one labelled: NO THROTTLING and the other labeled: SLOW 3G The "No Throttling' list is shorter than the "Slow 3G" list as the browser triggers appear earlier when the browser knows it needs more time to gather the required images

When won't it work?

* No Browser Support
* No JavaScript

Repeat view of the page with images of donuts stacked and the network window open, but this time JavaScript is disabled. However, the images are still loading

loading-attribute-polyfill

https://github.com/mfranzke/loading-attribute-polyfill

Code demo of Maximillian Franzke's polyfill solution for enabling lazy loading on non-supportive browsers

<noscript class="loading-lazy">
<img src="..." loading="lazy" width="250" height="150" />
</noscript>

Code demo of the JS check to determine whether lazy loading is supported in the browser

if ('loading' in HTMLImageElement.prototype) {
// supported ✔
} else {
// unsupported X
}

Code demo recap of how to apply the lazy loading property to an image

<img src="doughnut.jpg" alt="..." loading="lazy" />

Large yellow arrow is overlaid pointing to the "loading="lazy" attribute in the sequence

Code demo of where to apply the property within picture elements - not to the picture or source elements, but only to the image

<picture>
<source media="(min-width: 800px)" srcset="big-doughnut.jpg 1x,
bigger-doughnut.jpg 2x">
<img src="regular-doughnut.jpg" loading="lazy">
</picture>

Large yellow arrow is overlaid pointing to the "loading="lazy" attribute in the sequence

Code demo of where to apply the property within iframes

<iframe src"..." loading="lazy">

Large yellow arrow is overlaid pointing to the "loading="lazy" attribute in the sequence

Gotcha #1

Above the fold loading

Image of a screen web page interface with wireframe elements representing header, text, and image elements. A large square has a circle labelled "1." inside it. A graph underneath the screen denotes viewport width. Two large yellow arrows are overlaid on the diagram, one points to box number 1 and notes it is above the fold. The other points to the graph underneath the image and notes this area as below the fold. Lazy loading is not recommended for images above the fold as it slows load time

Gotcha #2

Hidden iFrames

Image of a screen web page interface with wireframe elements representing header, text, and image elements. A large yellow arrow is overlaid on the image pointing to an area with a question mark, noting that it represents a hidden iframe. Chrome won't lazy load hidden iframes

Gotcha #3

Background images

Image of a screen web page interface showing a pair of hands reading a folding brochure. A large yellow arrow is overlaid on the image pointing to an area where the paper folds, noting that it represents a background image. Lazy loading doesn't affect CSS-defined background images

Animated demo of two signup buttons on a screen, one in red labelled: Permanently Subscribe. and one in green labelled: Forget me.. Suddenly an image of a cat appears above the buttons, causing them to jump. The animation illustrates situations where lazy loaded images can cause other page elements to shift whilst loading if the width and height parameters of the image are not pre-defined

Image of Anton

Thank you