Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
November 29, 2021 03:44 pm GMT

How to load responsive images in AVIF and WebP using the HTML picture element

We released support for the new AVIF image format on the Contentful Images API at our Fast Forward 2021 conference. This means you can return your images stored in Contentful in the new AVIF format via the REST API with a query parameter, or via the GraphQL API with a transform function in your query.

But what is AVIF and should you be using it right now?

Now this stuff is tricky, so strap in and get ready for a wild ride! In this post, well cover:

  • Image formats, compression and browser support
  • Why image compression is important
  • The HTML <picture> element
  • Lazy loading and browser support
  • Performance improvements gained with AVIF

Are you ready? Lets go.

What is AVIF?

In short, AVIF is the new kid on the block when it comes to image compression. Released in 2019 by the Alliance for Open Media, AVIF claims to offer 30% better compression than current alternatives such as WebP. Its optimized for the web, designed with a low computation footprint, and whats more its open source.

Why is image compression important?

Websites in 2021 are an incredibly visual experience, full of images, animations and full-page photography. If youre not careful, you can end up forcing visitors to your website to download hundreds of megabytes of images on a visit to your page. This risks making your website slow, inevitably causing your visitors to bounce. And we dont want that!

A fast experience on the web is crucial to providing a great user experience for visitors to your website, and as of June 2021, Google uses Core Web Vitals scores to rank websites in search results. Core Web Vitals are currently scored on three aspects of user experience loading, interactivity and visual stability.

Loading

Loading performance is measured by the Largest Contentful Paint (LCP). (This has nothing to do with the Contentful CMS in this context! ). If youre forcing users to download megabytes of images when the page first starts to load for example, large hero images at the top of the page your LCP time will increase. To provide a good user experience, the LCP should happen within 2.5 seconds of when the page first starts loading.

Interactivity

Interactivity is measured by the First Input Delay (FID) and measures how soon your web application responds to user input such as clicking and typing into form fields. To provide a good user experience, pages should have an FID of 100 milliseconds or less.

Visual stability

Visual stability is measured by Cumulative Layout Shift (CLS). Have you ever clicked on a part of a web page, only to find that you unexpectedly clicked on something else after a rogue element or image was finally loaded? CLS is where content pops into view once it has loaded, often pushing content down or sideways on the web page and can be extremely frustrating! CLS makes your web page unstable and usually, large images that take time to load are to blame. A good user experience maintains a CLS score of 0.1 or less.

Why is image compression important?

Did you know that the average internet speed across the world in 2021 is only 55.13Mbs? Thats only marginally faster than the speed of a slow 3G connection as simulated in Chromium Dev Tools. If youre the nerdy type and want to look at the source code for the throttling simulations check out this link on GitHub!

A graph showing the average internet speed in the world in 2021 a surveyed by world population review dot com as 55.15Mbs.

In "Using Modern Image Formats: AVIF And WebP, Addy Osmani, engineering manager at Google Chrome, gives us this fascinating piece of data:

If youre optimizing for the Web Vitals, you might be interested to hear that images account for ~42% of the Largest Contentful Paint element for websites.

If almost 50% of your LCP is down to images you need to deliver them to your website visitors in as few bytes as possible. The smaller the image file sizes are on your web pages, the less time it takes for visitors to your websites to download them and this is especially important on slow internet connections and older, slower devices.

Minimizing the size of your images ensures your Core Web Vitals scores are as good as possible across the board, which means you provide a better experience for your users, and ultimately means your pages rank better in Google search results. And to minimise the size of your images you need to use the image format that yields the lightest results.

So if AVIF images currently offer the smallest image file sizes, we should all get on the AVIF train ASAP, right?

Should you convert all your images to AVIF right now?

WebP, there are some downsides to adopting this new format in 2021.

AVIF may not be able to compress non-photographic images as well as PNG or lossless WebP. I found this to be the case in my initial experiments with the hero image on the homepage of my personal website.

A screenshot from my Twitch stream, where I am looking to the left and smiling, with VSCode open in the background.

Whilst this image does contain photographic imagery of my face, most of the image is text and illustrations. As a WebP image, this image came in at 118kb, whilst as an AVIF image, the image size increased to 125kb.

Secondly, at the time of writing this article, not all browsers currently support the AVIF image format. Edge, Safari and some mobile browsers are not quite there yet.

A screenshot of can I use dot com showing that the support for AVIF is currently only available in Firefox, Chrome, Opera, Android and Samsung Internet browser.

But all is not lost!

There is a beautiful way we can harness the power of native HTML to serve different supported image formats to browsers using the HTML <picture> tag. And whats more, when other browsers do catch up with the AVIF train you wont need to change any code!

Before we look at the HTML, lets take a look at how you can convert your images stored in Contentful to the new AVIF format using the REST and GraphQL APIs.

How to convert your images to AVIF using the Contentful Images API

The Contentful Images API offers a variety of image transformations and manipulations as query parameters on the URL pointing to an image asset. You can change the quality of an image, resize it, add a background color, crop it, add rounded corners and more, as well as change the format of an image.

Using query parameters on an image URL

To convert your images stored in Contentful to AVIF, add fm=avif as a query parameter to your image URL. Well be using this technique later in the HTML example code.

https://images.ctfassets.net/{space_id}/{asset_id}/{unique_id}/{file_name}?fm=avif

Heres an example URL for you to play with in your browser. Experiment with changing the format parameter to jpg, png, webp, gif or avif and compare the size of the image returned in the network tab!

https://images.ctfassets.net/zz0ob82dbd6h/5YwujztZwQrte5WfgZf3eV/9eba3414d91e8e1e376fc96c0c8e6d63/cat-heavy-breathing-intensifies.gif?fm=avif

For the impatient among you, heres how the image formats compare in terms of size:

  • jpg: 76.8kb
  • png: 125kb
  • webp: 167kb
  • gif: 1.2MB
  • avif: 25.3kb

A stacked collage image showing the same heavy breathing cat gif in five image formats, showing that the avif image is the fewest bytes.

AVIF is the clear winner in terms of reduction in size for this image. However, one interesting thing to note is that whereas the original animated GIF comes in at a whopping 1.2MB, converting the image to WebP reduces the image size by a massive 87% and preserves the animation frames! I think thats a fascinating achievement and a wonderful nugget of information! I was so excited at this discovery that I shared it on Twitter!

Using GraphQL

If youre using GraphQL, you can convert your images to AVIF via a transform function on the image URL in the GraphQL query, like so:

query {  blogPostCollection(limit: 1) {    items {      image {        url(transform: { format: AVIF })      }    }  }}

And heres the response returned. Youll notice that this is the same image URL we used in the example above, demonstrating that the GraphQL API communicates directly with Contentfuls Images API to return what you need in your GraphQL response.

{  "data": {    "blogPostCollection": {      "items": [        {          "image": {            "url": "https://images.ctfassets.net/zz0ob82dbd6h/5YwujztZwQrte5WfgZf3eV/9eba3414d91e8e1e376fc96c0c8e6d63/cat-heavy-breathing-intensifies.gif?fm=avif"          }        }      ]    }  }}

However while you can retrieve images in AVIF format in GraphQL remember that not all browsers support this image format yet so you dont want to lock yourself down to the AVIF image format.

Now lets look at how we can take a base image URL and serve it in different ways to browsers that support different image formats using Contentfuls Images API and the native HTML <picture> tag.

And here is what youve all been waiting for!

How to serve responsive images with AVIF in supported browsers using the HTML tag

Ive attempted to understand the low-down on responsive images from MDN for many years and I must admit it was hard to grasp! This post from Stefan Judis provided a great help in getting to grips with this, in conjunction with MDN.

The "one does not simply" meme that reads: "One does not simply use width:100% to create responsive images".

Before we get into the code, we need to understand what we mean by responsive images. Responsive images are not simply about giving all images width: 100% to fill the size of a container! The real power in responsive images is serving different image files of different sizes to different viewport sizes and whats more screens with different pixel densities or display resolutions. This is called Resolution Switching, and by providing browsers with an array of image URL options by configuring a <source> elements srcset, media, and type attributes, the most compatible image according to the current layout and display capabilities will be displayed.

Lets get straight into the HTML code example, and unpack what it does.

<picture>  <source    type="image/avif"    srcset="      https://images.ctfassets.net/.../img.png?q=75&w=500&fm=avif 500w,      https://images.ctfassets.net/.../img.png?q=75&w=900&fm=avif 900w,      https://images.ctfassets.net/.../img.png?q=75&w=1300&fm=avif 1300w,      https://images.ctfassets.net/.../img.png?q=75&w=1700&fm=avif 1700w,    "    sizes="(max-width: 735px) 100vw, 736px"  />  <source    type="image/webp"    srcset="      https://images.ctfassets.net/.../img.png?q=75&w=500&fm=webp 500w,      https://images.ctfassets.net/.../img.png?q=75&w=900&fm=webp 900w,      https://images.ctfassets.net/.../img.png?q=75&w=1300&fm=webp 1300w,      https://images.ctfassets.net/.../img.png?q=75&w=1700&fm=webp 1700w    "    sizes="(max-width: 735px) 100vw, 736px"  />  <img    srcset="      https://images.ctfassets.net/.../img.png?q=75&w=500 500w,      https://images.ctfassets.net/.../img.png?q=75&w=900 900w,      https://images.ctfassets.net/.../img.png?q=75&w=1300 1300w,      https://images.ctfassets.net/.../img.png?q=75&w=1700 1700w    "    sizes="(max-width: 735px) 100vw, 736px"    src="https://images.ctfassets.net/.../img.png"    alt="Some great alternative text"    loading="lazy"    decoding="async"    width="2032"    height="1076"  /></picture>

HTML element

The <picture> HTML element wraps <source> elements and one <img> element to offer alternative versions of an image for different displays, devices or browsers (depending on the image formats supported).

The magic in the <picture> element is that the browser chooses the most appropriate <source> element to display. If no matches are found or the browser doesn't support the <picture> element the browser falls back to the URL in the src attribute of the <img> element. The selected image is then presented in the space occupied by the <img> element. To prevent Cumulative Layout Shift as the image is loading, its important to add the height and width attributes to the <img> element for this fallback.

<picture>  <source ... />  <source ... />  <img    src="https://images.ctfassets.net/.../img.png"    alt="Some great alternative text"    width="2032"    height="1076"  /></picture>

HTML element

The HTML <source> element provides multiple media types for the <picture> element to select the best-fit for the browser or device. You can also use the <source> element inside HTML <audio> or <video> elements in the same way.

In this example, the <source> elements are offering image formats in type="image/avif" and type="image/webp", which browsers will choose to display in that order depending on support.

<source type="image/avif"  .../><source type="image/webp"  .../>

HTML srcset and sizes attributes

The HTML srcset attribute is a comma-separated list of strings of image URLs and either a width descriptor such as 300w, or a pixel density descriptor such as 1.5x.

srcset="      https://images.ctfassets.net/.../img.png?q=75&w=500&fm=avif 500w,      https://images.ctfassets.net/.../img.png?q=75&w=900&fm=avif 900w,      https://images.ctfassets.net/.../img.png?q=75&w=1300&fm=avif 1300w,      https://images.ctfassets.net/.../img.png?q=75&w=1700&fm=avif 1700w    "

The HTML sizes attribute is a comma-separated list of media conditions paired with sizes to describe the final rendered image width in CSS pixels, not physical pixels (read more about different types of pixels on MDN). The browser wants to know the best-fit resources to request as the page is loading. The sizes attribute helps the browser to calculate the layout of the page and request the most suitable images for the layout even before the CSS has loaded.

sizes="(max-width: 735px) 100vw, 736px"

The srcset and sizes attributes work together in the browser to determine which image defined in the srcset to request as the page is being rendered or the viewport resized.

In this code example, four image URLs are provided in the srcset for four different image widths: 500w, 900w, 1300w, 1700w. Notice that the w={width} parameter on the image URLs is resizing the base image to match with size of the width descriptor using the capabilities of the Contentful Images API. When not using the Contentful Images API, you may wish to choose completely different image URLs at different viewport widths to serve different image sizes.

A note on the magic 736 number

For blog posts on my personal website, the maximum width of the image container will only ever be 736px in CSS pixels.

The sizes attribute states that under a viewport width of 736px, choose the most appropriate image from the srcset, considering that the image will be laid out across the entire viewport width (100vw). Depending on the device, full viewport width (100vw) could translate to 300 device pixels at 1 DPR, 600 device pixels at 2 DPR and 900 device pixels at 3DPR.

At a viewport width of 736px and above (remember, its 736 CSS pixels), the browser knows that the image widths will not exceed 736 CSS pixels as the CSS is controlling the container width. Depending on the device, 736 CSS pixels will be 1472px at 2DPR and 2208px at 3DPR. At all viewport widths, the browser will choose the closest match from the srcset list. Like magic!

This is an entirely personal example, and you should configure your image widths according to the layout of your web pages. You can also make further optimizations using CSS calc() to account for the padding values inside the container to load even smaller images at different viewport widths.

Its also important to note that the srcset and sizes attributes only come into effect when the <source> element is a direct child of a <picture> element.

<source    type="image/avif"    srcset="      https://images.ctfassets.net/.../img.png?q=75&w=500&fm=avif 500w,      https://images.ctfassets.net/.../img.png?q=75&w=900&fm=avif 900w,      https://images.ctfassets.net/.../img.png?q=75&w=1300&fm=avif 1300w,      https://images.ctfassets.net/.../img.png?q=75&w=1700&fm=avif 1700w    "    sizes="(max-width: 735px) 100vw, 736px"  />

HTML loading=lazy and decoding=async

Lazy loading is a strategy to identify resources such as images, video and other media as non-blocking, or non-critical and to load these only when needed, such as when a user scrolls that resource into view.

To prevent visitors to web pages downloading a full page of images that arent visible in the viewport yet, we can use native HTML lazy loading with the loading="lazy" attribute to defer image loading until a user scrolls near it no JavaScript required! As stated before, to prevent Cumulative Layout Shift, be sure to add the height and width of the base image as attributes to the <img> element, which will instruct the browser to leave the correct space on the page in which to finally load your image when it scrolls into view.

And in addition, the decoding="async" attribute tells the browser that the image loading can be performed asynchronously, so that content below the resource such as paragraphs of text are not blocked by the image taking a moment to load.

<img  ...  loading="lazy"  decoding="async"  width="2032"  height="1076"/>

Its worth a mention that loading="lazy" isnt entirely supported across all browsers yet, but at the time of writing this article, Safari just shipped it in the newest Tech Preview.

A screenshot from can I use dot com showing that lazy loading is supported only in Safari's newest tech preview. Firefox, chrome, Opera, Android, Edge, and Samsung Internet are shown to support it.

The code in action

Heres a video of the code example in action at 1 DPR. Notice the lazy-loading in action as the page is scrolled, and how the browser loads different image URLs from the srcset when the browser is resized! And let me reiterate no JavaScript required!

An animated image showing the network tab in Chromium dev tools is loading differently sizes images when the browser window is resized.

How did serving AVIF improve my website?

The code example above is a lot of HTML to write! But I like it for how self-documenting it is. And whats more, Ive bagged some significant performance improvements for my personal website while learning all about AVIF.

For image-heavy blog posts, and in particular this blog post, converting all images from WebP to AVIF reduced the cumulative size of images on a desktop screen at 2 DPR from just over 1Mb to just 404kb. And on mobile devices at 1 DPR, it reduced the cumulative sizes of images from 430kb to 124kb. Thats a 60-70% reduction across device sizes! Those are some huge savings! And plus, all images are lazy-loaded, which means the browser doesnt request them until the image is near to the visible viewport.

The bottom line

Responsive images on the web are a tricky business to get right! But if you get them right, you provide a better user experience for your website visitors across browsers, devices and screen sizes, and you get bonus points from Google in your Core Web Vitals scores.

If youd like to see the final code example as a React component in the code for my personal blog site, check it out on GitHub, and if you have any questions about responsive images and how you can make the best of the Contentful Images API, find me on Twitter!


Original Link: https://dev.to/whitep4nth3r/load-avif-webp-using-html-picture-tag-4576

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To