The lazy loading of images via the
loading
attribute has landed in
Chrome, and other browser-vendors are sure to follow suit. Deferring to the
browser when support is available and otherwise loading a polyfill such as
Lazysizes is a solid approach to
performant, responsive images.
Checking the HTMLImageElement
for the loading
property is a reliable way to
test for native lazy loading support:-
const supportsLoadingAttribute = 'loading' in HTMLImageElement.prototype;
If the browser supports native image loading we do nothing, or else we
dynamically import()
the Lazysizes module. Authoring this code within a client-side only
Nuxt plugin means the polyfill loads and
initialises only once and within the context of the entire application:-
// ~/plugins/lazysizes.client.js
export default () => {
if ('loading' in HTMLImageElement.prototype) {
return;
}
import('lazysizes');
};
Below is a loosely outlined ResponsiveImage
component which follows the
pattern that I want to demonstrate.
The server-side rendered HTML contains an image with the src
and srcset
values assigned to data-*
attributes – the actual attributes contain
placeholders. On mount()
(a client-side only
Vue lifecycle hook)
if the browser supports the loading
attribute the placeholders are replaced by
the true src
and srcset
values. If support is absent then the class
'lazyload'
is added to the <img>
and Lazysizes takes over from there:-
<!-- ~/components/ResponsiveImage.vue -->
<template>
<img
:class="{ lazyload: loading === 'lazy' && !supportsLoadingAttribute }"
:loading="loading"
v-bind="{ ...sources }"
/>
</template>
<script>
// base64-encoded transparent GIF
const placeholder =
'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
export default {
props: {
// the props required to compute `srcset` should go here
loading: {
type: String,
default: 'lazy',
},
},
data() {
return {
supportsLoadingAttribute: false,
};
},
computed: {
src() {
// `return` a fallback image for browsers
// that don't support `srcset` and `sizes`
},
srcset() {
// responsive images can be handled in all sorts of
// ways and I won't go into any further detail here
},
sources() {
if (this.loading === 'lazy' && !this.supportsLoadingAttribute) {
return {
'data-src': this.src,
'data-srcset': this.srcset,
src: placeholder,
srcset: `${placeholder} 1w`,
};
}
return {
srcset: this.srcset,
};
},
},
mounted() {
this.supportsLoadingAttribute = 'loading' in HTMLImageElement.prototype;
},
};
</script>
There's are many different approaches to lazy-loading images on the web. Each has its advantages and disadvantages and the one you choose will ultimately depend on your priorities. Are you more concerned about SEO, page speed, data footprint, or browser compatibility?
The pattern outlined above, for example, would need to provide a <noscript>
fallback in the case of JavaScript being disabled.
Either way, hopefully this has started you off in the right direction. Check out
the links below for some more in-depth explanations of the loading
attribute
and lazy-loading markup patterns.