picture и srcset

<picture> и srcset -- механизмы адаптивных изображений в HTML. Они позволяют браузеру выбрать оптимальный файл в зависимости от размера экрана, плотности пикселей и поддерживаемого формата.

Зачем нужно

Один и тот же <img src="photo.jpg"> загрузится и на iPhone (375px), и на 4K мониторе (3840px). Это расточительство трафика на мобильных или размытая картинка на Retina. srcset и <picture> решают это: браузер выбирает из нескольких вариантов, загружая ровно то, что нужно.

Где используется

  • Адаптивные изображения на любых сайтах
  • Art direction (разные кропы для разных экранов)
  • Современные форматы (WebP, AVIF) с fallback
  • Retina / HiDPI дисплеи
  • Оптимизация Core Web Vitals (LCP)

Предпосылки

srcset -- разные размеры одного изображения

По плотности пикселей (x-дескрипторы)

<!-- 1x для обычных экранов, 2x для Retina -->
<img
  src="photo-400.jpg"
  srcset="photo-400.jpg 1x,
          photo-800.jpg 2x,
          photo-1200.jpg 3x"
  alt="Фото продукта"
  width="400"
  height="300"
>

Браузер на Retina (2x) загрузит photo-800.jpg, на обычном -- photo-400.jpg.

По ширине изображения (w-дескрипторы) + sizes

<img
  src="photo-800.jpg"
  srcset="photo-400.jpg 400w,
          photo-800.jpg 800w,
          photo-1200.jpg 1200w,
          photo-1600.jpg 1600w"
  sizes="(max-width: 600px) 100vw,
         (max-width: 1200px) 50vw,
         33vw"
  alt="Фото продукта"
>

Как работает:

  1. sizes сообщает браузеру, какую ширину займёт изображение
  2. Браузер вычисляет нужный размер с учётом DPR (device pixel ratio)
  3. Из srcset выбирается ближайший подходящий файл
Экран 375px (1x DPR):
  sizes → 100vw = 375px
  Нужно: 375px → выберет photo-400.jpg (400w)

Экран 375px (2x DPR, Retina):
  sizes → 100vw = 375px
  Нужно: 375 * 2 = 750px → выберет photo-800.jpg (800w)

Экран 1400px (1x DPR):
  sizes → 33vw = 462px
  Нужно: 462px → выберет photo-800.jpg (800w)

picture -- art direction и форматы

Art direction (разные кропы)

<picture>
  <!-- Мобильные: вертикальный кроп -->
  <source
    media="(max-width: 600px)"
    srcset="hero-mobile.jpg 600w,
            hero-mobile-2x.jpg 1200w"
    sizes="100vw"
  >
  <!-- Планшеты: квадратный кроп -->
  <source
    media="(max-width: 1024px)"
    srcset="hero-tablet.jpg 1024w,
            hero-tablet-2x.jpg 2048w"
    sizes="100vw"
  >
  <!-- Desktop: широкий кроп -->
  <img
    src="hero-desktop.jpg"
    srcset="hero-desktop.jpg 1920w,
            hero-desktop-2x.jpg 3840w"
    sizes="100vw"
    alt="Панорама города"
  >
</picture>

Современные форматы с fallback

<picture>
  <!-- AVIF -- лучшее сжатие -->
  <source type="image/avif" srcset="photo.avif">
  <!-- WebP -- хорошее сжатие, широкая поддержка -->
  <source type="image/webp" srcset="photo.webp">
  <!-- JPEG -- fallback для всех браузеров -->
  <img src="photo.jpg" alt="Фото продукта">
</picture>

Комбинация: форматы + адаптивность

<picture>
  <!-- AVIF, адаптивный -->
  <source
    type="image/avif"
    srcset="photo-400.avif 400w,
            photo-800.avif 800w,
            photo-1200.avif 1200w"
    sizes="(max-width: 600px) 100vw, 50vw"
  >
  <!-- WebP, адаптивный -->
  <source
    type="image/webp"
    srcset="photo-400.webp 400w,
            photo-800.webp 800w,
            photo-1200.webp 1200w"
    sizes="(max-width: 600px) 100vw, 50vw"
  >
  <!-- JPEG fallback -->
  <img
    src="photo-800.jpg"
    srcset="photo-400.jpg 400w,
            photo-800.jpg 800w,
            photo-1200.jpg 1200w"
    sizes="(max-width: 600px) 100vw, 50vw"
    alt="Фото продукта"
    width="800"
    height="600"
    loading="lazy"
  >
</picture>

sizes -- подсказка браузеру

<!-- sizes сообщает: "изображение займёт такую ширину на экране" -->
<img
  srcset="small.jpg 400w, medium.jpg 800w, large.jpg 1200w"
  sizes="
    (max-width: 480px) 100vw,     /* На мобильных: вся ширина */
    (max-width: 1024px) 50vw,     /* На планшетах: половина */
    (max-width: 1200px) 33vw,     /* На десктопах: треть */
    400px                          /* По умолчанию: 400px */
  "
  src="medium.jpg"
  alt="Фото"
>

Важно: sizes -- это подсказка, а не CSS. Браузер не применяет эти размеры к изображению. Реальный размер определяется CSS.

Сравнение форматов

Формат   | Размер*  | Поддержка       | Когда использовать
---------|----------|-----------------|--------------------
AVIF     | ~50%     | 93%+ (2025)     | Когда поддержка есть
WebP     | ~70%     | 97%+            | Основной выбор
JPEG     | 100%     | 100%            | Fallback
PNG      | 150-300% | 100%            | Прозрачность (или WebP)
SVG      | -        | 100%            | Иконки, логотипы

* Относительно JPEG при одинаковом качестве

Автоматизация

# Генерация нескольких размеров через sharp-cli
npx sharp-cli resize 400 -i photo.jpg -o photo-400.jpg
npx sharp-cli resize 800 -i photo.jpg -o photo-800.jpg
npx sharp-cli resize 1200 -i photo.jpg -o photo-1200.jpg

# Конвертация в WebP
npx sharp-cli -i photo.jpg -o photo.webp --format webp --quality 80

# Конвертация в AVIF
npx sharp-cli -i photo.jpg -o photo.avif --format avif --quality 60

Частые ошибки

Ошибка Проблема Решение
Нет sizes при w дескрипторах Браузер считает 100vw Указать реальные sizes
Нет fallback <img> в <picture> Ничего не отобразится <img> обязателен
Нет alt на <img> Недоступно для SR Добавить описание
Нет width/height Layout shift (CLS) Указать размеры
Слишком много вариантов Сложность без пользы 3-4 размера достаточно
srcset без src Старые браузеры не покажут src как fallback

Практика

  1. Создай <img> с srcset и тремя размерами (400w, 800w, 1200w)
  2. Добавь sizes для мобильных, планшетов и десктопов
  3. Используй <picture> для WebP с JPEG fallback
  4. Реализуй art direction: разные кропы для мобильных и десктопов
  5. Проверь в DevTools (Network tab), какой файл загрузился

Связанные темы

Ресурсы