Skip to content

Responsive images

A responsive image lists several variants of the same image in a srcset, and lets the browser download the one that fits the screen. Picasso writes that list for you. The only thing it cannot guess is how wide the image is displayed in your layout: that is the sizes prop.

The image width depends on the viewport (full-width banner, column of a grid…). Give sizes, and the srcset lists every width of device_sizes and image_sizes, up to the source width:

<twig:Picasso:Image src="photo.jpg" width="800" height="600" sizes="(max-width: 768px) 100vw, 800px" alt="…" />
srcset="…?w=16… 16w, …?w=32… 32w, … , …?w=1920… 1920w"

sizes tells the browser how many CSS pixels the image takes, the screen density turns them into device pixels, and the browser takes the first variant at least that wide. With sizes="(max-width: 768px) 100vw, 800px":

Writing sizes:

  • Describe the layout, not the image: (max-width: 768px) 100vw, 800px reads “full width on small screens, 800 CSS pixels otherwise”.
  • The first matching condition wins: list the narrow screens first.
  • An approximation is fine. Too large wastes bytes, too small blurs the image.
  • In a grid of three columns: (max-width: 768px) 100vw, 33vw.

Each format of formats but the last becomes a <source>, tried in order, and the last one is the <img> fallback. The default [avif, webp, jpg] serves AVIF to browsers that support it, WebP to the others, and JPEG to the rest.

Every format multiplies the variants of each image: 16 widths × 3 formats is 48 renders for a crawler fetching them all. Trimming the widths your layout never uses, or dropping WebP when you serve AVIF, cuts the cost of a cold cache.

The browser reserves the space of an image from its width and height attributes, before it loads. Picasso only writes them when it knows both:

  • give both, or
  • give one, and let Picasso read the other from the source: this needs metadata resolution, on by default for filesystem loaders.