⌨️ Web development

Images for the web: formats, sizes and lazy loading

How to choose between JPEG, PNG, WebP, AVIF and SVG, serve the right size with srcset and sizes, and lazy load images without slowing the page.

On many pages images make up the heaviest part of the transfer. They are also the most common cause of slow loading of the main content and of sudden layout shifts. The good news is that modern HTML offers native tools to serve the right image to the right device, with no external libraries.

Choosing the format

Each format has an area where it performs best:

Format Ideal for Notes
JPEG photographs universal compatibility, no transparency
PNG graphics with transparency, screenshots lossless, often heavy for photos
WebP photos and graphics usually lighter than JPEG and PNG at the same quality
AVIF photos very efficient compression, slower encoding
SVG logos, icons, vector illustrations infinitely scalable, it is text and compresses well

For photographs, WebP and AVIF can reduce weight considerably compared with JPEG. For logos and icons, SVG is almost always the best choice: it stays sharp on any screen and weighs a few kilobytes. If the SVG comes from a graphics program, it is worth stripping unnecessary metadata before publishing it.

Offering several formats with picture

The picture element lets the browser pick the first format it supports:

<picture>
  <source srcset="/img/photo.avif" type="image/avif">
  <source srcset="/img/photo.webp" type="image/webp">
  <img src="/img/photo.jpg" alt="Description of the photo"
       width="1200" height="800">
</picture>

The final img is mandatory: it is the element actually displayed, with the alt, width and height attributes, while the source elements only change the source file.

Serving the right size

A common mistake is loading a 3000-pixel-wide image to display it in a 400-pixel box. The browser resizes it, but the user has still downloaded the whole file. The srcset and sizes attributes solve the problem:

<img
  src="/img/product-800.jpg"
  srcset="/img/product-400.jpg 400w,
          /img/product-800.jpg 800w,
          /img/product-1600.jpg 1600w"
  sizes="(max-width: 600px) 100vw, 50vw"
  alt="Brass table lamp"
  width="800" height="800">
  • srcset lists the available versions with their actual width (400w means 400 pixels).
  • sizes tells the browser how wide the image will be on the page: full width on screens up to 600 pixels, half the window otherwise.

With this information the browser works out which file to download, also taking the screen's pixel density into account. A phone with a high-density screen will choose a larger version than the CSS width alone suggests, and that is correct.

If you omit sizes, the browser assumes the image takes up the full width of the window and tends to download versions that are too large.

Reserving space

Setting width and height on the image is not about fixing its visual size (you handle that with CSS); it lets the browser calculate the aspect ratio before the download. It can then reserve the correct space, and the text below does not jump when the image arrives.

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

This rule, combined with the attributes in the HTML, keeps proportions and makes images fluid. For boxes with fixed proportions you can also use aspect-ratio and object-fit: cover.

Lazy loading: yes, but not everywhere

The native loading="lazy" attribute postpones downloading images until they get close to the visible area:

<img src="/img/gallery-12.webp" loading="lazy"
     alt="..." width="600" height="400">

It is perfect for galleries, long lists and images at the bottom of the page. It is harmful, however, for the main image at the top of the page: delaying it worsens perceived loading time. For that image use normal loading and, if it is the most important element, add fetchpriority="high".

decoding="async" can also help, letting the browser decode the image without blocking the rest of the rendering.

Alternative text

Optimising an image also means making it understandable. The alt attribute describes the content for screen reader users and appears when the image fails to load. A few rules:

  • describe what the image conveys in context, not every detail;
  • do not start with "image of": the screen reader already announces that it is an image;
  • for purely decorative images use alt="" (empty, not missing), so they are ignored;
  • if the image is a link, the alt must describe the destination.

Automating the work

Manually preparing three formats in four sizes for every image is not sustainable. There are two typical solutions:

  1. At build time: many static site generators and build tools have plugins that create the variants automatically and write the markup with srcset.
  2. Through a transformation service: some CDNs and image services generate the requested size and format on the fly via URL parameters and cache the result.

In both cases, keep the high-quality originals: they will let you regenerate the variants when you change the layout or a better format appears.

In short

  • Use SVG for logos and icons, WebP or AVIF for photographs, with JPEG as a fallback.
  • srcset and sizes let the browser download only the size it needs.
  • Always set width and height to avoid layout shifts.
  • Apply loading="lazy" to images far from the top of the page, never to the main one.
  • Write useful alternative text and automate the generation of variants.
MM
Marco Marzaduri

Marco Marzaduri works in cybersecurity, digital forensics and web development. A computer science graduate, he combines rigorous technical analysis with attention to the people who use technology.

Read also