Native HTML Images and Lazy Loading

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

See the section bibliography for the reference material consulted while preparing these pages.

Baseline date: 2026-10-07. The browser already knows how to pick a file, delay a download and avoid layout jumps, if the markup gives it the information. This page lists the attributes that matter. The server side (which variants to produce and which URLs to return) is in responsive images and thumbnails, and the general loading-performance background is in performance and loading. Reference pages at MDN: image format guide and responsive images.

A complete example

<picture>
  <source type="image/avif"
          srcset="/covers/b81f/w320.avif 320w, /covers/b81f/w640.avif 640w, /covers/b81f/w1280.avif 1280w"
          sizes="(min-width: 60em) 20vw, 50vw">
  <source type="image/webp"
          srcset="/covers/b81f/w320.webp 320w, /covers/b81f/w640.webp 640w, /covers/b81f/w1280.webp 1280w"
          sizes="(min-width: 60em) 20vw, 50vw">
  <img src="/covers/b81f/w640.jpg"
       srcset="/covers/b81f/w320.jpg 320w, /covers/b81f/w640.jpg 640w, /covers/b81f/w1280.jpg 1280w"
       sizes="(min-width: 60em) 20vw, 50vw"
       width="640" height="960"
       alt="Cover of Moby-Dick"
       loading="lazy" decoding="async">
</picture>

The attributes

srcset and sizes

srcset lists candidate files with their intrinsic width (640w) or pixel density (2x). sizes tells the browser how wide the image will be on screen at each viewport width, so it can choose before layout. Without sizes the browser assumes the image is as wide as the viewport and downloads too much. Use width descriptors for fluid layouts and density descriptors for fixed-size images such as avatars.

width and height

Give the intrinsic size. Browsers use the ratio to reserve space before the image loads, which prevents layout shift (CLS). Set the CSS to height: auto; max-width: 100% so the image still scales. The same applies to <video>.

<picture> and type

The browser takes the first <source> whose type it supports, so list AVIF first, WebP second, and keep the <img> as the JPEG fallback. The <img> is required, since it is the element that is rendered, and every attribute that is not about source selection (alt, width, height, loading, decoding, fetchpriority) goes on it.

loading="lazy"

Defers offscreen images until they are near the viewport. It is supported natively by current browsers, with no JavaScript. Never put it on the image that is likely the largest contentful paint (LCP) element, usually the hero image or the first cover: lazy loading delays it and worsens LCP. Everything below the first screen should be lazy. Reference: MDN loading.

decoding="async"

Lets the browser decode the image off the main thread and display other content first. It is safe on almost every image. Use decoding="sync" only when you need the image painted in the same frame as the surrounding content.

fetchpriority

fetchpriority="high" on the LCP image tells the browser to fetch it ahead of other images. fetchpriority="low" on carousel slides and below-the-fold images helps bandwidth go to the right place. Use it sparingly: if everything is high, nothing is. Reference: MDN fetchpriority.

The LCP image

<img src="/hero/w1280.jpg" srcset="/hero/w640.jpg 640w, /hero/w1280.jpg 1280w, /hero/w1920.jpg 1920w"
     sizes="100vw" width="1920" height="1080" alt="Library reading room"
     fetchpriority="high" decoding="async">

The image has no loading="lazy" and has fetchpriority="high". To start the download even earlier, add a <link rel="preload" as="image"> element to the head with the imagesrcset and imagesizes attributes.

A placeholder while the image loads

The width and height reserve an empty box. To fill it, show a BlurHash while the file downloads, see BlurHash in the browser. A cheap CSS-only alternative is the average colour of the image as background-color on the <img>, which the backend can return next to the BlurHash.

Video

<video controls playsinline preload="none" width="1280" height="720"
       poster="/videos/42/poster-1280.avif">
  <source src="/videos/42/720p.webm" type='video/webm; codecs="vp9, opus"'>
  <source src="/videos/42/720p.mp4" type='video/mp4; codecs="avc1.64001f, mp4a.40.2"'>
  Your browser cannot play this video.
</video>
  • poster is the image shown before playback. Generate it from a frame, see video covers and thumbnails, and serve it in the modern formats like any image (the attribute takes one URL, so choose AVIF or WebP only if you accept that older browsers show nothing, or keep a JPEG).

  • preload="none" downloads nothing until the user presses play, which is right for lists of videos. preload="metadata" fetches the duration and dimensions only. preload="auto" lets the browser buffer, and wastes bandwidth when most visitors do not play.

  • playsinline keeps playback inline on iOS rather than fullscreen. Add muted for autoplay, which browsers only allow for muted video.

  • For several quality levels, use adaptive streaming rather than several <source> files, see video playback and streaming.

A quick audit

  1. Does every <img> have width, height and alt?

  2. Does every image below the first screen have loading="lazy", and the LCP image does not?

  3. Does sizes match the real CSS width of the slot?

  4. Do the <picture> sources list AVIF and WebP with a JPEG fallback?

  5. Do the Lighthouse and Chrome DevTools Performance panels show the LCP element loaded early, and CLS near zero? See web.dev on LCP.