Что такое lazy loading
Lazy loading — загрузка изображений только когда они видны или близки к viewport-у пользователя.
Без lazy: при загрузке страницы браузер скачивает все 30 изображений карточки товара или каталога. Это 2-5 МБ трафика и 1-2 секунды задержки до интерактивности.
С lazy: грузятся только видимые сейчас (3-5 штук). Остальные подгружаются по мере прокрутки.
Native loading="lazy" — самый простой способ
В 2026 году у вас есть встроенный в HTML атрибут:
<img src="photo.jpg" loading="lazy" alt="Описание" />Поддержка:
- Chrome, Edge — с 2019
- Safari — с 2022
- Firefox — с 2020
- Все мобильные браузеры — да
То есть 95%+ пользователей автоматически получают lazy loading. Без JavaScript, без библиотек.
Когда НЕ использовать lazy
❌ Hero-изображение (above the fold)
Самое крупное изображение в первом экране — никогда не lazy.
<!-- ПЛОХО -->
<img src="hero.jpg" loading="lazy" alt="Hero" />
<!-- ХОРОШО -->
<img src="hero.jpg" loading="eager" fetchpriority="high" alt="Hero" />Lazy для hero → ухудшение LCP, потеря Core Web Vitals.
❌ Первые 1-3 изображения на странице
Обычно это «выше сгиба», их нужно показать сразу.
❌ Иконки и маленькие декоративные изображения
Они не «весят», lazy для них не имеет смысла и добавляет сложность.
fetchpriority="high" — для критических
Hero и важные изображения нужно загружать в первую очередь:
<img
src="hero.jpg"
fetchpriority="high"
loading="eager"
alt="Главное изображение"
/>fetchpriority="high" говорит браузеру: «приоритизируй эту картинку выше всех остальных ресурсов».
Это даёт +0.3-0.8 сек к LCP в типовых случаях.
width и height — обязательно
Любое изображение в 2026 году обязано иметь явные width и height:
<img
src="photo.jpg"
width="800"
height="600"
loading="lazy"
alt="Описание"
/>Без них:
- Браузер не знает размер до загрузки
- При загрузке изображения контент сдвигается
- CLS (Cumulative Layout Shift) ухудшается
- Core Web Vitals падает
С width/height браузер резервирует место заранее → нет сдвигов.
Современные форматы — WebP и AVIF
В 2026 году JPG и PNG — устарели для веба.
| Формат | Размер vs JPG | Совместимость |
|---|---|---|
| JPG | базовый | 100% |
| PNG | +50% (без прозрачности) | 100% |
| WebP | -30-40% | 96%+ |
| AVIF | -50-60% | 91%+ |
Стратегия: отдавать AVIF / WebP / JPG в зависимости от браузера через `<picture>`:
<picture>
<source srcset="photo.avif" type="image/avif" />
<source srcset="photo.webp" type="image/webp" />
<img
src="photo.jpg"
width="800"
height="600"
loading="lazy"
alt="Описание"
/>
</picture>Responsive images через srcset
Для разных размеров экрана отдаём разные размеры изображения:
<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: 768px) 100vw, 800px"
width="800"
height="600"
loading="lazy"
alt="Описание"
/>Браузер сам выбирает подходящий размер под устройство пользователя. Мобильный получает 400px, десктоп — 1200px.
Next.js Image — автоматизация
В Next.js есть встроенный компонент Image, который делает всё автоматически:
import Image from 'next/image';
<Image
src="/photo.jpg"
alt="Описание"
width={800}
height={600}
priority={isAboveTheFold}
/>Что делает автоматически:
- Конвертирует в WebP/AVIF
- Генерирует разные размеры (srcset)
- Lazy loading по умолчанию
- При
priority—fetchpriority="high" - Резервирует место под изображение
Это стандарт де-факто для Next.js-проектов.
IntersectionObserver — для кастомных случаев
Native loading="lazy" запускается, когда изображение близко к viewport. По умолчанию расстояние большое (3000px для мобильных, 1250px для десктопа).
Если нужно более агрессивный lazy (загружать только когда изображение видно) — IntersectionObserver:
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
}, { rootMargin: '50px' });
document.querySelectorAll('img[data-src]').forEach(img => {
observer.observe(img);
});Анти-паттерны
❌ Lazy для всех изображений
Hero и outsource выше сгиба нужно загружать сразу.
❌ Lazy без width/height
CLS ломается. Layout прыгает.
❌ Картинки в формате PNG для фото
JPG/WebP/AVIF — для фотографий. PNG — только для логотипов и иконок с прозрачностью.
❌ Несжатые изображения
10 МБ JPG в 2026 году — это преступление. Сжимать через tinypng.com, squoosh.app или CI/CD пайплайн.
❌ Картинки с фиксированной width="600" под все экраны
На мобильных это слишком большая картинка, грузится дольше нужного.
Используйте srcset.
❌ Lazy через JS, когда есть native loading="lazy"
Лишняя сложность. Native работает быстрее.
Влияние на Core Web Vitals
Правильный lazy loading даёт:
- LCP: -0.5-1 сек (улучшение)
- CLS: 0 (если есть width/height)
- INP: улучшение от меньшей нагрузки на main thread
- TTFB: не меняется
Неправильный lazy (на hero) — наоборот ухудшает LCP на 0.5-1.5 сек.
Чек-лист
- [ ]
loading="lazy"на изображениях ниже первого экрана - [ ]
loading="eager"+fetchpriority="high"на hero - [ ]
widthиheightна каждом изображении - [ ] Современный формат: WebP или AVIF
- [ ] srcset для responsive
- [ ] alt-атрибуты на всех изображениях
- [ ] PNG только для логотипов/иконок
- [ ] Сжатие через tinypng/squoosh
- [ ] Проверка LCP в PageSpeed Insights
Итог
Lazy loading в 2026 году — встроенный в HTML механизм. 90% случаев решает loading="lazy". Для hero — fetchpriority="high". Всё остальное — детали для оптимизации.