How to Use WebP Images in HTML

A practical guide to serving WebP images on your website — with fallbacks for older browsers, responsive images, and background image techniques.

Published January 17, 2024

WebP images load faster and use less bandwidth than JPEG or PNG. Here’s how to use them correctly in HTML — including fallbacks for the rare browser that doesn’t support WebP, responsive techniques, and CSS background images.

The simplest case: <img> with a WebP file

If you’re targeting modern browsers (Chrome, Firefox, Safari 14+, Edge), you can use WebP directly in an <img> tag just like any other format:

<img src="hero.webp" alt="A description of the image" width="1200" height="630" />

Always include width and height attributes. They allow the browser to reserve the correct space before the image loads, preventing layout shift (a Core Web Vitals metric).

WebP browser support is above 97% as of 2024, so for most public websites this is all you need.

Adding a fallback with <picture>

If you need to support older browsers (Safari 13 and below, IE11), the <picture> element lets you offer multiple formats and let the browser pick the best one it supports:

<picture>
  <source srcset="hero.webp" type="image/webp" />
  <img src="hero.jpg" alt="A description of the image" width="1200" height="630" />
</picture>

How it works:

  • Modern browsers that understand WebP load hero.webp
  • Browsers that don’t support WebP skip the <source> and load hero.jpg from the <img> tag
  • The <img> tag is always required — it’s the fallback and the element that carries alt, width, height, and other attributes

The type="image/webp" attribute tells the browser what format the source is, so it can skip it without downloading anything if it doesn’t support that type.

Responsive WebP images

Combine <picture> with srcset and sizes to serve the right image size for each screen:

<picture>
  <source
    type="image/webp"
    srcset="hero-480.webp 480w, hero-800.webp 800w, hero-1200.webp 1200w"
    sizes="(max-width: 600px) 480px, (max-width: 900px) 800px, 1200px"
  />
  <source
    type="image/jpeg"
    srcset="hero-480.jpg 480w, hero-800.jpg 800w, hero-1200.jpg 1200w"
    sizes="(max-width: 600px) 480px, (max-width: 900px) 800px, 1200px"
  />
  <img
    src="hero-1200.jpg"
    alt="A description of the image"
    width="1200"
    height="630"
  />
</picture>

The browser evaluates both the type (can it decode WebP?) and the srcset (which size fits the viewport?) to pick the single best file to download.

Lazy loading

Add loading="lazy" to defer loading images that are below the fold:

<img src="card.webp" alt="..." loading="lazy" width="400" height="300" />

Don’t use loading="lazy" on images that are visible above the fold (hero images, logos). Those should load immediately. Use it for images in grids, below content sections, or anywhere the user would need to scroll to see them.

For the most important above-the-fold image, add fetchpriority="high" instead:

<img src="hero.webp" alt="..." fetchpriority="high" width="1200" height="630" />

Preloading critical WebP images

If your largest above-the-fold image is a WebP, add a preload hint in <head> so the browser discovers and fetches it as early as possible:

<link
  rel="preload"
  as="image"
  href="hero.webp"
  type="image/webp"
/>

For responsive preloads, include imagesrcset and imagesizes:

<link
  rel="preload"
  as="image"
  href="hero-1200.webp"
  imagesrcset="hero-480.webp 480w, hero-800.webp 800w, hero-1200.webp 1200w"
  imagesizes="(max-width: 600px) 480px, (max-width: 900px) 800px, 1200px"
  type="image/webp"
/>

WebP as a CSS background image

CSS doesn’t have a built-in mechanism for format negotiation like <picture>. There are two approaches:

1. Serve WebP to everyone (simplest)

If you only need to support modern browsers:

.hero {
  background-image: url('hero.webp');
}

2. Use JavaScript to detect WebP and set a class

<script>
  const img = new Image();
  img.onload = () => document.documentElement.classList.add('webp');
  img.onerror = () => document.documentElement.classList.add('no-webp');
  img.src = 'data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoBAAEAAUAmJaQAA3AA/vz0AAA=';
</script>

Then in your CSS:

.hero { background-image: url('hero.jpg'); }
.webp .hero { background-image: url('hero.webp'); }

3. Use image-set() (modern CSS approach)

.hero {
  background-image: image-set(
    url('hero.webp') type('image/webp'),
    url('hero.jpg') type('image/jpeg')
  );
}

image-set() is supported in Chrome 88+, Firefox 90+, and Safari 15.4+. Check caniuse.com for current coverage before using it without a fallback.

Checking your WebP images are actually serving

Open your browser DevTools, go to the Network tab, filter by “Img”, and look at the Type column. You should see webp for any WebP files being served. You can also check the Response Headers for content-type: image/webp.


Ready to convert?

Use our free in-browser converters to create WebP files from your existing images:


Related: What is WebP? · WebP vs PNG vs JPG