Most Optimized Web Image Loading in 2021



In this article, I will share 8 techniques for optimizing image loading that reduce the required network bandwidth and processor load when displayed on the screen. Here are some examples of annotated HTML to make it easier for you to reproduce. Some techniques have been known for a long time, and some have appeared relatively recently. Ideally, your favorite web document publishing mechanism (such as a CMS, static site generator, or web application framework) should do all of this out of the box.



Collectively, the techniques optimize all elements of Google Core Web Vitals by:





To see all the techniques in action, take a look at the source code for loading this image:



https://www.industrialempathy.com/img/remote/ZiClJf.jpg
<img loading="lazy" decoding="async" style="background-size: cover; background-image: none;" src="/img/remote/ZiClJf.avif" alt="Sample image illustrating the techniques outlined in this post." width="4032" height="2268">

      
      







Optimization techniques



Responsive layout



This straightforward technique allows the image to occupy the available horizontal space while maintaining the aspect ratio. In 2020, browsers have learned to reserve the correct amount of vertical space for an image before it loads if the element img



contains attributes width



and height



. This avoids the cumulative shift of the layout.



<style>
 img {
   max-width: 100%;
   height: auto;
 }
</style>
<!-- Providing width and height is more important than ever. -->
<img height="853" width="1280" … />

      
      





Lazy rendering



The second technique is more complicated. The new CSS attribute content-visibility: auto



tells the browser not to think about placing the image until it's ready. This approach has several advantages, the main one of which is that until the browser receives a blurred placeholder image or the image itself, it will not decode it, saving processor resources.



No longer need contain-intrinsic-size



An earlier version of the article explained how to contain-intrinsic-size



avoid the CLS effect when using content-visibility: auto



. But in Chromium 88 this is no longer necessary in the case of images for which width



and height



. As of January 27, 2021, content-visibility: auto



not yet implemented in other browser engines
, they are likely to follow Chromium's lead. So yeah, it's much easier now!



<style>
 /* This probably only makes sense for images within the main scrollable area of your page. */
 main img {
   /* Only render when in viewport */
   content-visibility: auto;
 }
</style>

      
      





AVIF



AVIF is the most recent graphics format that has received support in browsers. It is now supported in Chromium and by flag in Firefox. Safari doesn't work with it yet, but since Apple is part of the group that developed the format, this browser should also support AVIF in the future.



This format is remarkable in that it is vastly superior to JPEG. And this compares favorably with the WebP format, the images in which are not always smaller than JPEG and which can increase resource consumption due to the lack of support for progressive loading.



To implement a progressive extension for AVIF, you can use picture



.



The element is actually img



nested in picture



... This can be confusing, because it is img



sometimes called a fallback solution for browsers that do not support picture



, but in fact this element only helps with the choice src



, and it does not have its own layout. The element img



will be drawn , and you will apply the style to it.



Until recently, it was quite difficult to implement AVIF images on the server side, but recent versions of libraries like sharp made this task much easier.



<picture>
 <source
   sizes="(max-width: 608px) 100vw, 608px"
   srcset="
     /img/Z1s3TKV-1920w.avif 1920w,
     /img/Z1s3TKV-1280w.avif 1280w,
     /img/Z1s3TKV-640w.avif   640w,
     /img/Z1s3TKV-320w.avif   320w
   "
   type="image/avif"
 />
 <!-- snip lots of other stuff -->
 <img />
</picture>

      
      





Loading the correct number of pixels



The above code has attributes srcset



and sizes



. They use a selector w



to tell the browser which URL to take based on the physical number of pixels it takes to render the image on a particular device. This amount depends on the width of the image, which is calculated based on the attribute sizes



(which is an expression from the media query).



This ensures that the browser will always load the smallest image possible, providing the best quality on a particular device. Or he can select the smallest image if the user has enabled data save mode.



Fallback solution



For browsers that only support older image formats, you can srcset



provide more raw elements with the help of:



<source
 sizes="(max-width: 608px) 100vw, 608px"
 srcset="
   /img/Z1s3TKV-1920w.webp 1920w,
   /img/Z1s3TKV-1280w.webp 1280w,
   /img/Z1s3TKV-640w.webp   640w,
   /img/Z1s3TKV-320w.webp   320w
 "
 type="image/webp"
/>
<source
 sizes="(max-width: 608px) 100vw, 608px"
 srcset="
   /img/Z1s3TKV-1920w.jpg 1920w,
   /img/Z1s3TKV-1280w.jpg 1280w,
   /img/Z1s3TKV-640w.jpg   640w,
   /img/Z1s3TKV-320w.jpg   320w
 "
 type="image/jpeg"
/>

      
      





Caching and immutable URLs



Embed in the image URL a hash of the number of bytes that the image occupies. In the example above, I did it with Z1s3TKV



. When you change the image, the URL will also change, which means you can apply infinite caching of images. Caching headers should look like cache-control: public,max-age=31536000,immutable



.



immutable



Is a semantically correct meaning cache-control



, but it has little browser support today (I'm looking at you, Chrome). max-age=31536000



- fallback caching method throughout the year. public



is needed for your CDN to cache the image and deliver it from the network edge. But this approach can only be used if it does not violate your privacy policies.



Lazy loading



By adding loading=«lazy»



to the element, img



we tell the browser to start fetching the image only when it is ready to be rendered.



<img loading="lazy" … />

      
      





Asynchronous decryption



By adding decoding=«async»



to the element, img



we allow the browser to decrypt the image outside the main stream so that this procedure does not interfere with the user. There should be no noticeable flaws in this solution, except that it is not always applicable by default in older browsers.



<img decoding="async" … />

      
      





Blurred stub



A fuzzy stub is an inline image that gives the user some idea of ​​a full-fledged picture that will be loaded later, without transferring data over the network.



https://www.industrialempathy.com/img/blurry.svg





A few implementation notes:



  • The stub is inlined like background-image



    images. This technique lets you drop the second HTML element by literally hiding the stub when the main image is loaded, no JavaScript is needed.
  • The main image data URI is wrapped in the SVG image data URI. This is done because the blur is done at the SVG level and not using a CSS filter. That is, the blur is done once for each image when rasterized by SVG, not for each layout. This saves processor resources.


<img
 style="
     …
     background-size: cover;
     background-image:
       url('data:image/svg+xml;charset=utf-8,%3Csvg xmlns=\'http%3A//www.w3.org/2000/svg\'

xmlns%3Axlink=\'http%3A//www.w3.org/1999/xlink\' viewBox=\'0 0 1280 853\'%3E%3Cfilter id=\'b\' color-interpolation-filters=\'sRGB\'%3E%3CfeGaussianBlur stdDeviation=\'.5\'%3E%3C/feGaussianBlur%3E%3CfeComponentTransfer%3E%3CfeFuncA type=\'discrete\' tableValues=\'1 1\'%3E%3C/feFuncA%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Cimage filter=\'url(%23b)\' x=\'0\' y=\'0\' height=\'100%25\' width=\'100%25\'
       xlink%3Ahref=\'data%3Aimage/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAGCAIAAACepSOSAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAs0lEQVQI1wGoAFf/AImSoJSer5yjs52ktp2luJuluKOpuJefsoCNowB+kKaOm66grL+krsCnsMGrt8m1u8mzt8OVoLIAhJqzjZ2tnLLLnLHJp7fNmpyjqbPCqLrRjqO7AIeUn5ultaWtt56msaSnroZyY4mBgLq7wY6TmwCRfk2Pf1uzm2WulV+xmV6rmGyQfFm3nWSBcEIAfm46jX1FkH5Djn5AmodGo49MopBLlIRBfG8yj/dfjF5frTUAAAAASUVORK5CYII=\'%3E%3C/image%3E%3C/svg%3E');
   "
 …
/>

      
      





(Optional) JavaScript optimization



Browsers may be forced to rasterize the blurry stub even if the image is already loaded. The problem can be solved by removing the rasterization at boot. In addition, if your image has transparent areas, then this optimization becomes mandatory, otherwise a stub will show through the image.



<sript>
 document.body.addEventListener(
   "load",
   (e) => {
     if (e.target.tagName != "IMG") {
       return;
     }
     // Remove the blurry placeholder.
     e.target.style.backgroundImage = "none";
   },
   /* capture */ true
 );
</sript>

      
      





Additionally



A useful tool that implements all the described optimizations: eleventy-high-performance-blog



All Articles