Image performance

Images are often the heaviest and most prevalent resource on the web. As a result, optimizing images can significantly improve performance on your website. In most cases, optimizing images means reducing the network time by sending fewer bytes, but you can also optimize the amount of bytes sent to the user by serving images that are properly sized for the user's device.

Images can be added to a page using the <img> or <picture> elements, or the CSS background-image property.

Image size

The first optimization you can perform when it comes to using image resources is to display the image at the correct size—in this case, the term size refers to the dimensions of an image. Considering no other variables, an image displayed in a 500 pixel by 500 pixel container would be optimally sized at 500 pixels by 500 pixels. For example, using a square 1000 pixel image means that the image is twice as large as needed.

However, there are many variables involved in choosing the proper image size, making the task of choosing the proper image size in every case to be quite complicated. In 2010, when the iPhone 4 was released, the screen resolution (640x960) was double that of the iPhone 3 (320x480). However, the physical size of the iPhone 4's screen remained roughly the same as the iPhone 3.

Displaying everything at the higher resolution would have made text and images significantly smaller—half their previous size to be exact. Instead, 1 pixel became 2 device pixels. This is called the device pixel ratio (DPR). The iPhone 4—and many iPhone models released after it—had a DPR of 2.

Revisiting the earlier example, if the device has a DPR of 2 and the image is displayed in a 500 pixel by 500 pixel container, then a square 1000 pixel image (referred to as the intrinsic size) is now the optimal size. Similarly, if the device has a DPR of 3, then a square 1500 pixel image would be the optimal size.

srcset

The <img> element supports the srcset attribute, which lets you specify a list of possible image sources the browser may use. Each image source specified must include the image URL, and a width or pixel density descriptor.

<img
  alt="An image"
  width="500"
  height="500"
  src="/image-500.jpg"
  srcset="/image-500.jpg 1x, /image-1000.jpg 2x, /image-1500.jpg 3x"
>

The preceding HTML snippet uses the pixel density descriptor to hint the browser to use image-500.png on devices with a DPR of 1, image-1000.jpg on devices with a DPR of 2, and image-1500.jpg on devices with a DPR of 3.

While this all may seem cut and dry, a screen's DPR is not the only consideration in choosing the optimal image for a given page. The page's layout is yet another consideration.

sizes

The previous solution only works if you display the image at the same CSS pixel size on all viewports. In many cases, the layout of a page—and the container's size with it—changes depending on the user's device.

The sizes attribute lets you specify a set of source sizes, where each source size consists of a media condition and a value. The sizes attribute describes the intended display size of the image in CSS pixels. When combined with the srcset width descriptors, the browser can choose which image source is best for the user's device.

<img
  alt="An image"
  width="500"
  height="500"
  src="/image-500.jpg"
  srcset="/image-500.jpg 500w, /image-1000.jpg 1000w, /image-1500.jpg 1500w"
  sizes="(min-width: 768px) 500px, 100vw"
>

In the preceding HTML snippet, the srcset attribute specifies a list of image candidates the browser can choose from, separated by commas. Each candidate in the list consists of the image's URL, followed by a syntax that denotes the intrinsic width of the image. An image's intrinsic size is its dimensions. For example, a descriptor of 1000w denotes that the image's intrinsic width is 1000 pixels wide.

Using this information, the browser evaluates the media condition in the sizes attribute, and—in this case—is instructed that if the device's viewport width exceeds 768 pixels, the image is displayed at a width of 500 pixels. On smaller devices, the image is displayed at 100vw—or the full viewport width.

The browser can then combine this information with the list of srcset image sources to find the optimal image. For example, if the user is on a mobile device with a screen width of 320 pixels with a DPR of 3, the image is displayed at 320 CSS pixels x 3 DPR = 960 device pixels. In this example, the closest sized image would be image-1000.jpg which has an intrinsic width of 1000 pixels (1000w).

File formats

Browsers support several different image file formats. Modern image formats like WebP and AVIF may provide better compression than PNG or JPEG, making your image file size smaller and therefore taking less time to download. By serving images in modern formats, you can reduce a resource's load time, which may result in a lower Largest Contentful Paint (LCP).

WebP is a widely supported format that works on all modern browsers. WebP often has better compression than JPEG, PNG, or GIF, offering both lossy and lossless compression. WebP also supports alpha channel transparency even when using lossy compression—a feature the JPEG codec doesn't offer.

AVIF is a newer image format, and while it isn't as widely supported as WebP, it does enjoy reasonably decent support across browsers. AVIF supports both lossy and lossless compression, and tests have shown greater than 50% savings when compared to JPEG in some cases. AVIF also offers Wide Color Gamut (WCG) and High Dynamic Range (HDR) features.

Compression

Where images are concerned, there are two types of compression:

  1. Lossy compression
  2. Lossless compression

Lossy compression works by reducing the image accuracy through quantization, and additional color information may be discarded using chroma subsampling. Lossy compression is most effective on high-density images with lots of noise and colors—typically photos or imagery with similar contents. This is because the artifacts produced by lossy compression are much less likely to be noticed in such detailed images. However, lossy compression may be less effective with imagery containing sharp edges such as line art, similarly stark details, or text. Lossy compression can be applied to JPEG, WebP, and AVIF images.

Lossless compression reduces the file size by compressing an image with no data loss. Lossless compression describes a pixel based on the difference from its neighboring pixels. Lossless compression is used for the GIF, PNG, WebP, and AVIF image formats.

You can compress your images using Squoosh, ImageOptim, or an image optimization service. When compressing, there isn't a universal setting suitable for all cases. The recommended approach would be to experiment with different compression levels until you find a good compromise between image quality and file size. Some advanced image optimization services can do this for you automatically, but may not be financially viable for all users.

The <picture> element

The <picture> element gives you greater flexibility in specifying multiple image candidates:

<picture>
  <source type="image/avif" srcset="image.avif">
  <source type="image/webp" srcset="image.webp">
  <img
    alt="An image"
    width="500"
    height="500"
    src="/image.jpg"
  >
</picture>

When you use <source> element(s) within the <picture> element, you can add support for AVIF and WebP images, but fall back to more compatible legacy image formats if the browser does not support modern formats. With this approach, the browser picks the first <source> element specified that matches. If it can render the image in that format, it uses that image. Otherwise, the browser moves on to the next specified <source> element. In the preceding HTML snippet, the AVIF format takes priority over the WebP format, falling back to the JPEG format if neither AVIF or WebP is supported.

A <picture> element requires an <img> element nested inside of it. The alt, width, and height attributes are defined on the <img> and used regardless of which <source> is selected.

The <source> element also supports the media, srcset, and sizes attributes. Similarly to the <img> example earlier, these indicate to the browser which image to select on different viewports.

<picture>
  <source
    media="(min-resolution: 1.5x)"
    srcset="/image-1000.jpg 1000w, /image-1500.jpg 1500w"
    sizes="(min-width: 768px) 500px, 100vw"
  >
  <img
    alt="An image"
    width="500"
    height="500"
    src="/image-500.jpg"
  >
</picture>

The media attribute takes a media condition. In the preceding example, the device's DPR is used as the media condition. Any device with a DPR greater than or equal to 1.5 would use the first <source> element. The <source> element tells the browser that, on devices with a viewport wider than 768 pixels, the selected image candidate is displayed at 500 pixels wide. On smaller devices, this takes up the entire viewport width. By combining the media and srcset attributes, you can have finer control over which image to use.

This is illustrated in the following table, where several viewport widths and device pixel ratios are evaluated:

Viewport Width (pixels) 1 DPR 1.5 DPR 2 DPR 3 DPR
320 500.jpg 500.jpg 500.jpg 1000.jpg
480 500.jpg 500.jpg 1000.jpg 1500.jpg
560 500.jpg 1000.jpg 1000.jpg 1500.jpg
1024 500.jpg 1000.jpg 1000.jpg 1500.jpg
1920 500.jpg 1000.jpg 1000.jpg 1500.jpg

Devices with a DPR of 1 download the image-500.jpg image, including most desktop users—who view the image at an extrinsic size of 500 pixels wide. On the other hand, mobile users with a DPR of 3 download a potentially larger image-1500.jpg—the same image used on desktop devices with a DPR of 3.

<picture>
  <source
    media="(min-width: 560px) and (min-resolution: 1.5x)"
    srcset="/image-1000.jpg 1000w, /image-1500.jpg 1500w"
    sizes="(min-width: 768px) 500px, 100vw"
  >
  <source
    media="(max-width: 560px) and (min-resolution: 1.5x)"
    srcset="/image-1000-sm.jpg 1000w, /image-1500-sm.jpg 1500w"
    sizes="(min-width: 768px) 500px, 100vw"
  >
  <img
    alt="An image"
    width="500"
    height="500"
    src="/image-500.jpg"
  >
</picture>

In this example, the <picture> element is adjusted to include an additional <source> element to use different images for wide devices with a high DPR:

Viewport Width (pixels) 1 DPR 1.5 DPR 2 DPR 3 DPR
320 500.jpg 500.jpg 500.jpg 1000-sm.jpg
480 500.jpg 500.jpg 1000-sm.jpg 1500-sm.jpg
560 500.jpg 1000-sm.jpg 1000-sm.jpg 1500-sm.jpg
1024 500.jpg 1000.jpg 1000.jpg 1500.jpg
1920 500.jpg 1000.jpg 1000.jpg 1500.jpg

With this additional query, you can see that image-1000-sm.jpg and image-1500-sm.jpg are displayed on small viewports. This extra information lets you compress images further, since compression artifacts are not highly visible at that size and density, while also not compromising the image quality on desktop devices.

Alternatively, by adjusting the srcset and media attributes, you can avoid serving large images on small viewports:

<picture>
  <source
    media="(min-width: 560px)"
    srcset="/image-500.jpg, /image-1000.jpg 2x, /image-1500.jpg 3x"
  >
  <source
    media="(max-width: 560px)"
    srcset="/image-500.jpg 1x, /image-1000.jpg 2x"
  >
  <img
    alt="An image"
    width="500"
    height="500"
    src="/image-500.jpg"
  >
</picture>

In the preceding HTML snippet, the width descriptors have been removed in favor of device pixel ratio descriptors. Images served on a mobile device are limited to /image-500.jpg or /image-1000.jpg, even on devices with a DPR of 3.

How to manage complexity

When working with responsive images, you can find yourself with many different size variations and formats for each image. In the preceding example, variations for each size are used, but exclude AVIF and WebP. How many variants should you have? Like many engineering problems, the answer tends to be "it depends".

While it may be tempting to have as many variations to deliver the best fit, every additional image variant comes at a cost and makes less efficient use of the browser cache. With only one variant, every user receives the same image, so it can be cached very efficiently.

On the other hand, if there are many variations, each variant requires another cache entry. Server costs can increase and may degrade performance if the variant's cache entry has expired, and the image needs to be fetched again from the origin server.

Apart from this, the size of your HTML document grows with each variation. You could find yourself shipping multiple kilobytes of HTML for each image.

Serve images based on the Accept request header

The Accept HTTP request header informs the server which content types the user's browser understands. This information can be used by your server to serve the optimal image format without adding extra bytes to your HTML responses.

if (request.headers.accept) {
  if (request.headers.accept.includes('image/avif')) {
    return reply.from('image.avif');
  } else if (request.headers.accept.includes('image/webp')) {
    return reply.from('image.webp');
  }
}

return reply.from('image.jpg');

The preceding HTML snippet is a simplified version of the code you can add to your server's JavaScript backend to choose and serve the optimal image format. If the request Accept header includes image/avif, then the AVIF image is served. Otherwise, if the Accept header includes image/webp, the WebP image is served. If neither of these conditions is true, then the JPEG image is served.

You can modify responses based on contents of the Accept request header in nearly every type of web server—for example, you can rewrite image requests on Apache servers based on the Accept header using mod_rewrite.

This is not unlike behavior you would find on an Image Content Delivery Network (CDN). Image CDNs are excellent solutions for optimizing images and sending the optimal format based on the user's device and browser.

The key is to find a balance, generate a reasonable number of image candidates, and measure the impact on the user experience. Different images can give different results, and the optimizations applied to each image depend on its size within the page and the devices your users are using. For example, a full-width hero image may require more variants than thumbnail images on an ecommerce product listing page.

Lazy loading

It's possible to tell the browser to lazy load images when they appear in the viewport using the loading attribute. An attribute value of lazy tells the browser to not download the image until it is in (or near) the viewport. This saves bandwidth, allowing the browser to prioritize the resources it needs to render the critical content that is already in the viewport.

decoding

The decoding attribute tells the browser how it should decode the image. A value of async tells the browser that the image can be decoded asynchronously, possibly improving the time to render other content. A value of sync tells the browser that the image should be presented at the same time as other content. The default value of auto allows the browser to decide what is best for the user.

Image demos

Test your knowledge

Which image formats support lossless compression?

JPEG.
PNG.
GIF.
WebP.
AVIF.

Which image formats support lossy compression?

PNG.
JPEG.
AVIF.
GIF.
WebP.

What does the width descriptor (for example, 1000w) tell the browser about an image candidate specified in an srcset attribute?

The intrinsic width of the image—that is, the dimensions of the image itself.
The extrinsic width of the image—that is, the dimensions of the image in the layout after styles have been applied to the page

What does the sizes attribute tell the browser about an <img> element it's applied to?

The intrinsic width of the image to be loaded from the <img> element's srcset attribute.
Logic that expresses which candidate specified in an <img> element's srcset should be loaded, given the dimensions of the user's current viewport.

Up next: Video performance

While images may be the most prevalent media type used on the web, they're far from the only one you need to keep in mind when it comes to performance. Video is another common type of media used across the web, and comes with its own performance considerations. In the next module of this course, explore some techniques around optimizing videos and how to load them efficiently.