Sergio Xalambrí

Optimize image loading with plain HTML

If you're building some page like a landing with some images, you may want to optimize how these images load to ensure the page loads as fast as possible. Let's see how we could leverage different features of HTML to do that.

The plain image

Let's start with the simplest way to load an image.

<img src="/image.jpg" alt="a description of the image" />

This is our image, but it's not optimized at all. It may even cause a layout shift.

Add the size

The first thing we need to do is add the image size to the tag using the width and height attributes.

<img
  src="/image.jpg"
  alt="a description of the image"
  width="1000"
  height="400"
/>

Note that the size we define doesn't need to be the size the image will use when displayed on our landing. We can still change that with CSS, for example, by doing.

img {
  height: auto;
  max-width: 100%;
}

Now our image will try to use the size available of the container.

The reason to add the size as a fixed value in the HTML tag is to let the browser know the image's aspect ratio.

This way, if the browsers know that the image will be rendered inside a container of 500px.

They can also calculate that the height should be 200px and keep the size the image will need as the image load to avoid a layout shift.

Use a better format

Instead of using jpg or png we could use a more optimized format like webp or avif.

webp is currently supported on all modern browsers, but avif is only supported on Chrome and Firefox. So we could start using webp directly.

<img
  src="/image.webp"
  alt="a description of the image"
  width="1000"
  height="400"
/>

Load a different version based on the pixel density

Some screens have a different pixel density, like a retina screen. If we want to load an image with a pixel density of 2, we could use the srcset attribute.

Some devices may have other pixel densities too. We should also support 3x and 4x.

<img
  src="/image@4x.webp"
  alt="a description of the image"
  width="1000"
  height="400"
  srcset="
    /image.webp    1x,
    /image@2x.webp 2x,
    /image@3x.webp 3x,
    /image@4x.webp 4x
  "
/>

The srcset attribute receives the different versions of the image, and the 1x defines the pixel density we aim for with that version of the image. The src should load the larger image to ensure it works on every device if the srcset is not supported by the browser, which is not going to happen in all modern browsers anyway.

Load a different version based on the screen size

Another thing we could do is to change the image based on the screen size. This usually happens if the image on mobile has to change completely.

For example, we could show an image with more height and width on mobile and desktop, one with more width than height to accommodate the device screen.

We could also use srcset to do that, but we can't have a different version per pixel density. To combine the image, we could use the picture tag.

<picture>
  <!-- this will be the desktop image -->
  <source
    srcset="
      /desktop.webp    1x,
      /desktop@2x.webp 2x,
      /desktop@3x.webp 3x,
      /desktop@4x.webp 4x
    "
    media="(min-width: 768px)"
    width="1000"
    height="500"
  />
  <!-- This will be the mobile image -->
  <source
    srcset="
      /mobile.webp    1x,
      /mobile@2x.webp 2x,
      /mobile@3x.webp 3x,
      /mobile@4x.webp 4x
    "
    media="(max-width: 768px)"
    width="5000"
    height="1000"
  />
  <!-- the fallback is picture is not supported -->
  <img
    src="/image@4x.webp"
    alt="a description of the image"
    width="1000"
    height="400"
    srcset="
      /image.webp    1x,
      /image@2x.webp 2x,
      /image@3x.webp 3x,
      /image@4x.webp 4x
    "
  />
</picture>

This way, we have a source for desktop and another for mobile, each with its own srcset loading a different image per pixel density.

And we still have the img tag, which works as a fallback if picture is not supported and lets us set the width, height, and alt text. We should do it there if we had to add a CSS class.

Preload the image

The next thing we could do is to preload the image. When the browser is still parsing the head tag of the HTML document, it can start to load the image. Once it reaches the picture tag, it may have already downloaded or started to load the image.

To do this, we can use the link tag.

<link rel="preload" href="/desktop.webp" as="image" />

This is the primary way to use it, but we also have to load a different image per pixel density and screen size in our case. If we use the link above, we will always load the desktop image for 1x, even if it's not needed.

<link
  rel="preload"
  as="image"
  href="/desktop@4x.webp"
  imagesrcset="/desktop.webp 1x,
 /desktop@2x.webp 2x,
 /desktop@3x.webp 3x,
 /desktop@4x.webp 4x"
  media="(min-width: 768px)"
/>
<link
  rel="preload"
  as="image"
  href="/mobile@4x.webp"
  imagesrcset="/mobile.webp 1x,
 /mobile@2x.webp 2x,
 /mobile@3x.webp 3x,
 /mobile@4x.webp 4x"
  media="(max-width: 768px)"
/>