DEV Community

Cover image for Native lazy-loading of images with zero Javascript
Paul Facklam
Paul Facklam

Posted on

Native lazy-loading of images with zero Javascript

About lazy-loading and why you should use it

The worldwide web has evolved over the past decades and today's websites consist not only of text and color, but also of countless media content. First and foremost images.

<img src="/path/to/your/image.jpg" alt="Awesome image" />
Enter fullscreen mode Exit fullscreen mode

But what does this have to do with lazy-loading?

Lazy-loading is a technique that defers loading of non-critical resources at page load time. Instead, these non-critical resources are loaded at the moment of need. Where images are concerned, "non-critical" is often synonymous with "off-screen".
-- from web.dev/lazy-loading written by Jeremy Wagner and Rachel Andrew

This means by using lazy-loading we achieve the following benefits over the classic embedding in HTML:

  • Performance Gains - With lazy-loading we can improve loading speed by reducing the number of images that need to be loaded initially.
  • Cost reduction - A lazy loaded image may never need to load because the user never reaches the image's position on the page.

Ok, sounds good. How can we make that happen? What do we need for this? Let's get started!

The old (Javascript) approach

There are a lot of snippets and scripts like vanilla-lazyload out there that enable lazy-loading via javascript. In almost all cases a data attribute is used to prevent upfront image loading.

<img data-src="/path/to/your/image.jpg" alt="Awesome image" />
Enter fullscreen mode Exit fullscreen mode

But how is the image loaded at all? To achieve this, one of the following two techniques is usually used.

Event listeners

This technique uses event listeners on the scroll, resize and orientationChange events in the browser. If one of the mentioned events is fired and assuming the image enters the viewport the data-src attribute is replaced with the src attribute to trigger the loading call. See it in action:

Intersection Observer API

Unlike the first method, the image is observed (asynchronously) by using the IntersectionObserver API. The image is then loaded by changing the data-src to src attribute as soon as it enters the viewport.

But what if I told you that you don't need Javascript at all? 😲 Yes, you heard right! Zero Javascript.

The new (HTML) approach

So what does this new way of lazy-loading look like? No worries, it's perfectly simple. You just have to add loading="lazy" to your image tag and that's it. 😎

<img src="/path/to/your/image.jpg" loading="lazy" />
Enter fullscreen mode Exit fullscreen mode

With this new loading attribute, you can completely defer the loading of offscreen images (and even iframes in some browsers) to when they enter the viewport. Enough talking, let's see it in action!

The attribute comes with three values:

  • auto - (Default) equal to not including the attribute.
  • lazy - Defer loading of resources until it enters the viewport.
  • eager - Load the resource immediately

What about browser support?

The attribute is supported by almost every popular browser (Chrome, Edge, Opera and Firefox). The implementation for Safari is in progress and almost done. If you need more detailed information on cross-browser support caniuse.com is your friend.

What happens if a browser does not support the attribute?

Browsers that do not support the loading attribute simply ignore it without any side-effects. Fortunately, there is a polyfill at Github available named loading-attribute-polyfill which can be used in those cases.

if ('loading' in HTMLImageElement.prototype) {
    // Cool! The browser supports the loading attribute
} else {
   // Houston... We need a polyfill!
}
Enter fullscreen mode Exit fullscreen mode

Are there any limitations?

Even though it is pretty cool to let the browser do the lazy-loading work, you should be aware of some limitations that come into play when using the attribute:

  • Unlike the Javascript variants, you do not have any influence on the threshold to load the resource. It is part of the browser code and can't be changed for now.
  • The attribute can't be used in combination with CSS background images. Maybe it will come in near future but it is definitely not available now.
  • If printing the webpage is a real usecase for you, then please notice that there is an open bug for the loading attribute.

If you liked this post, please give me a ❀️ or even a πŸ¦„ and feel free to follow me on dev.to. Appreciate it! ✌️

pfacklam image

Discussion (23)

Collapse
madsstoumann profile image
Mads Stoumann • Edited on

As a sidenote, loading=β€œlazy” only works if JavaScript is enabled, which for me seemed a bit weird - until I read this at MDN:

Loading is only deferred when JavaScript is enabled. This is an anti-tracking measure, because if a user agent supported lazy loading when scripting is disabled, it would still be possible for a site to track a user's approximate scroll position throughout a session, by strategically placing images in a page's markup such that a server can track how many images are requested and when.

To test, disable JavaScript in DevTools (Shift + Command + P > Disable JavaScript), go to the Network Tab and refresh the page. All images will load instantly.

Collapse
pfacklam profile image
Paul Facklam Author

Interesting, didn't know that. Thanks for mentioning, Mads!

Collapse
brycewray profile image
Bryce Wray

Link where that's found:
developer.mozilla.org/en-US/docs/W...

Collapse
andrewbridge profile image
Andrew Bridge

Probably worth mentioning that this is best used for images which aren't going to be visible on page load. I've actually observed page performance in Lighthouse, and in turn Google's search console ranking, drop if there are too many images "above the fold" using loading="lazy" as it detects it as a longer initial load time.

It's a great feature though, definitely good for articles or long marketing pages.

Collapse
pfacklam profile image
Paul Facklam Author

Good point, Andrew! I will take that into account in future. Thanks for pointing out.

Collapse
liukonen profile image
Luke Liukonen

Great to know! Going to implement this on my personal page right after work and see how it goes

Collapse
liukonen profile image
Luke Liukonen

I wish I could report how great it worked for me, and how I was able to remove another 3rd party dependency from my site. Unfortunately, Vue 2 just doesn't get it and loads the images anyway. I tested it out on a "basic" site and it worked fantastic, however, if you are running a "framework"... in my case, Vue.. mileage may vary

Collapse
madsstoumann profile image
Mads Stoumann

That's probably because parts of the DOM gets re-written. You could try <img data-src="your-image" loading="lazy" />, and then set img.src = img.dataset.src, when the component has been mounted in Vue.

Thread Thread
pfacklam profile image
Paul Facklam Author

Looks like a mixture from both worlds (see above in the article). As a disclaimer: I haven't give Vue.js a try. Should I? πŸ€”

Thread Thread
liukonen profile image
Luke Liukonen • Edited on

Mads I might have to, thanks! I've been using a combination of Intersection observers, and a plugin, Vue.LazyLoad to handle the late binding of images on my page. It's just disappointing to see native HTML not render like you'd expect to see, but that might be one of the sacrifices you give up when using a framework.

Paul. Since Im just starting out with JS frameworks, I started with Vue. It is growing in popularity, however, React and Angular are still kings at this time.

Thread Thread
pfacklam profile image
Paul Facklam Author

Luke. I am very familiar with React, Angular and even Ember. But I haven't had the chance to look into Vue.js yet.

Collapse
ahmedsamirdev profile image
ahmedsamirdev

How it went?

Collapse
pfacklam profile image
Paul Facklam Author

Awesome! Maybe you can share your feedback how it worked for you. πŸ‘

Collapse
steinhoefel1 profile image
Thomas Kohler β˜‘οΈ Online-Marketing-Expert

Everything is #SEO: users and Google are happy.

Collapse
pfacklam profile image
Paul Facklam Author

That is absolutely correct! πŸ˜„

Collapse
briang123 profile image
Brian Gaines

Not great for safari

Collapse
pfacklam profile image
Paul Facklam Author

Hi Brian! What do you mean by that?

Collapse
briang123 profile image
Brian Gaines

just that the native lazy attribute on the image tag is not supported out of the box on Safari (it's an experimental item). I've had to implement alternative solutions for lazy as a result.

Thread Thread
pfacklam profile image
Paul Facklam Author

Yes, agree! That's is a little bit annoying. With luck, it might not be that long before we can use it natively here, too. Fingers 🀞!

Collapse
_dermatz profile image
Mathias Elle πŸ‘¨πŸ½β€πŸ’»

Safari, worst browser after IE 🀬

Collapse
pfacklam profile image
Paul Facklam Author

True words. So sad. 😁

Collapse
ben profile image
Ben Halpern

Great post

Collapse
pfacklam profile image
Paul Facklam Author

Thanks, Ben! Glad that you like it. 😊