Modern Image Formats on the Web: WebP and AVIF Without the Headache
Tired of seeing this Google Lighthouse diagnostic?

Those fancy modern image formats that supposedly only work in Chrome? Maybe you've considered using progressive enhancement—serving AVIF to capable browsers and falling back to JPEGs for the rest—but it all sounds a bit too complicated?
Let's take a proper look at what's possible in 2025 and how easy it actually is to use modern formats.
🧱 HTML Implementation: The <picture>
Element
Let's start with the trusty <picture>
tag:
<picture>
<source type="image/avif" srcset="/image.avif 1000w">
<source type="image/webp" srcset="/image.webp 1000w">
<img loading="lazy" decoding="async" src="/image.jpeg" alt="My image" width="1000" height="1000">
</picture>
The <picture>
element allows the browser to choose the best image format: AVIF, WebP, or JPEG as a fallback. The loading="lazy"
attribute delays loading the image until it's near the viewport, which helps improve page load speed. The decoding="async"
attribute decodes the image in the background, allowing the page to render faster without waiting for the image to finish decoding. While the number 1000
here is only an example, explicitly setting width
and height
helps the browser allocate the correct layout space before the image loads, which improves visual stability and avoids layout shifts—especially important for Core Web Vitals. Additionally, the srcset
attribute allows the browser to choose the most appropriate image based on screen size and resolution.
According to w3schools, browser support for <picture>
is excellent.
🎨 CSS Option: image-set()
for Backgrounds
If you're dealing with background images instead of inline <img>
elements, CSS offers a similar mechanism:
.element {
background-image: image-set(
url("image.avif") type("image/avif"),
url("image.webp") type("image/webp"),
url("image.jpg") type("image/jpeg")
);
}
This is supported in Safari 17+, as seen on Can I use.
For most use cases, you'll be covered using either <picture>
for inline images or image-set()
for backgrounds. However, because browser support for <picture>
is more consistent, it's usually the safer choice. When possible, avoid using background images for critical content.
In October 2024, I talked about the image-set()
CSS functional notation with my Working Draft Co-Host Schepp in the episode Revision 635: State of CSS 2024, Teil 3/3.
🛠️ Converting Images to WebP and AVIF
So how do you actually get your images into WebP or AVIF? While online tools exist, they're often a black box—you don't always know what optimizations or metadata stripping might be happening behind the scenes. Not to mention the privacy aspect of uploading your files.
If you're more comfortable on the command line and prefer open-source tooling, you're in luck.
🎬 Conversion via ffmpeg
On macOS (or any system with ffmpeg installed), you can convert a JPEG to AVIF with:
ffmpeg -i image.jpg -c:v libaom-av1 -crf 30 -b:v 0 image.avif
Let's break down what each part of this command does:
ffmpeg
:
This is the command-line tool for processing video, audio, and images. It supports a variety of formats and codecs, making it an excellent choice for converting media files.-i image.jpg
:
This specifies the input file. In this case, we’re using image.jpg as the source file. The -i option tells ffmpeg which file to process.-c:v libaom-av1
:
This option sets the codec to use for the video (or in this case, the image). libaom-av1 is the encoder for the AVIF format, specifically using the AV1 codec, which is known for excellent compression and quality.-crf 30
:
The Constant Rate Factor (CRF) controls the output quality. Lower CRF values result in better quality (and larger file sizes), while higher CRF values reduce the quality and the file size. In this case, 30 is a reasonable value for a balance between quality and file size. Typically, CRF values range from 0 (best quality) to 51 (worst quality).-b:v 0
:
This option controls the target bitrate. When set to 0, it tells ffmpeg to use variable bitrate encoding, where the bitrate adjusts based on the complexity of the image to maintain a consistent quality throughout. For AVIF, this is usually set to 0 for the best results.image.avif
:
This is the output file. ffmpeg will take the input file image.jpg, convert it, and save it as image.avif.
🧪 Conversion via Sharp (Node.js)
Instead of manually converting files every time, consider scripting it—either as a part of your build pipeline or as a simple CLI tool. For this, create a script to convert your images in your project using Sharp, a fast image processing library for Node.js.
I published a repository node-convert-sharp with a basic script. Here's a streamlined version of such a script:
#!/usr/bin/env node
import sharp from 'sharp';
import path from 'path';
import fs from 'fs/promises';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const inputPath = path.join(__dirname, 'input.jpg');
const outputDir = path.join(__dirname, 'output');
await fs.mkdir(outputDir, { recursive: true });
const baseName = path.basename(inputPath, path.extname(inputPath));
// Convert to WebP
try {
await sharp(inputPath)
.toFormat('webp')
.toFile(path.join(outputDir, `${baseName}.webp`));
console.log('✅ WebP success');
} catch (err) {
console.error('❌ Error WebP:', err);
}
// Convert to AVIF
try {
await sharp(inputPath)
.toFormat('avif')
.toFile(path.join(outputDir, `${baseName}.avif`));
console.log('✅ AVIF success');
} catch (err) {
console.error('❌ Error AVIF:', err);
}
🧰 Running the Script
-
Install sharp:
npm install sharp
-
Ensure your
package.json
includes:{ "type": "module" }
-
Make the script executable:
chmod +x convert.js
-
Run it:
./convert.js
Because of the #!/usr/bin/env node
shebang at the top, there's no need to call node convert.js
explicitly—the OS will handle it for you.
✅ Final Thoughts
Using modern image formats doesn't have to be a headache. With good browser support, native HTML and CSS solutions, and reliable open-source conversion tools, there's little excuse not to start using WebP and AVIF today.
Faster pages, smaller files, happier users.