The srcset Attribute

Provide multiple image files at different sizes. The browser picks the best one:

<img
  src="image-800.jpg"
  srcset="image-400.jpg 400w,
          image-800.jpg 800w,
          image-1200.jpg 1200w"
  alt="Description"
/>

The w descriptor tells the browser the intrinsic width of each file (in pixels). The browser uses this with the display width to calculate which file is most appropriate.

The sizes Attribute

Tell the browser how wide the image will be at different breakpoints so it can make a smarter choice:

<img
  src="card-800.jpg"
  srcset="card-400.jpg 400w, card-800.jpg 800w, card-1200.jpg 1200w"
  sizes="(max-width: 640px) 100vw,
         (max-width: 1024px) 50vw,
         400px"
  alt="Tool card image"
/>

Read as: on screens ≤640px wide, the image takes 100% of viewport width. On screens ≤1024px, it takes 50%. Otherwise it's 400px wide.

The picture Element

Use <picture> to serve different image formats or art-directed crops:

<!-- Serve WebP to browsers that support it, fallback to JPG -->
<picture>
  <source srcset="hero.webp" type="image/webp"/>
  <source srcset="hero.avif" type="image/avif"/>
  <img src="hero.jpg" alt="Hero image"/>
</picture>

<!-- Different crops for different screens -->
<picture>
  <source media="(max-width: 640px)" srcset="hero-mobile.jpg"/>
  <source media="(max-width: 1024px)" srcset="hero-tablet.jpg"/>
  <img src="hero-desktop.jpg" alt="Hero image"/>
</picture>

Native Lazy Loading

<!-- Browser handles lazy loading natively -->
<img src="photo.jpg" loading="lazy" alt="Photo"/>

<!-- Always eager-load above-the-fold images -->
<img src="hero.jpg" loading="eager" alt="Hero"/>

<!-- Prevent layout shift with width/height -->
<img src="photo.jpg" width="800" height="600" loading="lazy" alt="Photo"/>
💡 Always set width and height on images to prevent layout shift (CLS). The browser uses these to reserve the right amount of space before the image loads — even with CSS height: auto.