Why Image Format Matters
Images are typically 50-70% of a web page's total byte weight. Picking the right format is often the highest-leverage performance improvement available.
| Format | Browser Support | Compression | Best For |
|---|---|---|---|
| JPEG | Universal | Baseline | Photos (legacy) |
| PNG | Universal | Lossless | Logos, screenshots |
| WebP | 97%+ | 25-35% better than JPEG | Photos, illustrations |
| AVIF | 93%+ | 40-50% better than JPEG | Photos at small sizes |
| SVG | Universal | Vector (scalable) | Icons, logos |
WebP: The Safe Modern Default
When to use: Almost everywhere you'd use JPEG or PNG today.
WebP delivers 25-35% smaller files at the same visual quality. It supports:
- Lossy compression (like JPEG)
- Lossless compression (like PNG)
- Transparency (like PNG)
- Animation (like GIF, but smaller)
Browser support is now 97%+ — it's the safe choice.
Converting to WebP
# cwebp (Google's tool)
cwebp -q 85 input.jpg -o output.webp
# ImageMagick
convert input.jpg -quality 85 output.webp
# Sharp (Node.js)
const sharp = require('sharp')
await sharp('input.jpg').webp({ quality: 85 }).toFile('output.webp')
AVIF: Better Compression, Slower Encoding
AVIF is based on the AV1 video codec. It delivers 40-50% better compression than JPEG at the same quality — noticeably better than WebP for photographs.
Trade-offs:
- Better compression than WebP
- Slower encoding (2-5x slower than WebP)
- Browser support: 93%+ (slightly less than WebP)
- Decoding can be slower on older/lower-end devices
When to use AVIF:
- Hero images where file size matters most
- E-commerce product images (dozens per page)
- Any photo-heavy content where quality = brand
When to stick with WebP:
- Images that need fast encoding (user uploads, dynamic generation)
- Animations
- When you need maximum browser compatibility
The <picture> Element: Serve Both
Use <picture> to serve AVIF to capable browsers, WebP as fallback, JPEG as final fallback:
<picture>
<source srcset="/hero.avif" type="image/avif" />
<source srcset="/hero.webp" type="image/webp" />
<img src="/hero.jpg" alt="Hero image" width="1200" height="600" loading="lazy" />
</picture>
The browser picks the first format it supports. AVIF-capable browsers get AVIF; others get WebP; legacy browsers get JPEG.
Responsive Images with srcset
Combine format choice with responsive sizes:
<picture>
<source
type="image/avif"
srcset="/hero-400.avif 400w, /hero-800.avif 800w, /hero-1200.avif 1200w"
sizes="(max-width: 600px) 400px, (max-width: 900px) 800px, 1200px"
/>
<source
type="image/webp"
srcset="/hero-400.webp 400w, /hero-800.webp 800w, /hero-1200.webp 1200w"
sizes="(max-width: 600px) 400px, (max-width: 900px) 800px, 1200px"
/>
<img
src="/hero-1200.jpg"
alt="Hero"
width="1200"
height="600"
loading="lazy"
/>
</picture>
A mobile user on a 400px screen gets a 400px image instead of a 1200px image — massive savings.
Automating with Sharp (Node.js)
Generate all formats and sizes at build time:
const sharp = require('sharp')
const sizes = [400, 800, 1200]
const formats = ['avif', 'webp', 'jpeg']
async function processImage(inputPath, outputDir) {
for (const size of sizes) {
for (const format of formats) {
const ext = format === 'jpeg' ? 'jpg' : format
await sharp(inputPath)
.resize(size)
[format]({ quality: format === 'avif' ? 60 : 85 })
.toFile(`${outputDir}/image-${size}.${ext}`)
}
}
}
Quality Settings by Format
| Format | Recommended Quality | Notes |
|---|---|---|
| JPEG | 75-85 | Below 75 shows artifacts |
| WebP (lossy) | 80-85 | Equivalent to JPEG 85+ |
| WebP (lossless) | N/A | Use for graphics with text |
| AVIF | 50-70 | Lower = smaller, AVIF holds quality well |
AVIF can go much lower on quality settings before looking bad — quality 60 in AVIF often looks better than quality 85 in JPEG.
Key Takeaways
- WebP is the safe default — 25-35% smaller, 97% browser support
- AVIF is better for photos — 40-50% smaller, use where encoding time isn't critical
- Use
<picture>with multiple<source>elements to serve both - Combine with
srcsetto serve appropriately-sized images to each device - Automate generation with Sharp or Squoosh CLI during build