I audited a client's e-commerce site last year. Their PageSpeed mobile score was 31. Every product page loaded a 3.2MB hero image — a raw export from their camera — scaled down by CSS to 800px wide. After converting that single image to WebP at quality 80 and adding a proper width attribute, the score jumped to 67. Full image optimization across all pages got them to 91. All their previous speed work — caching, minification, CDN — had been undermined by one raw image per page.
Compress images locally — no upload needed.
Convert to WebP or AVIF, resize, and compress in your browser. Your images never leave your device.
Open Image Compressor →Why Images Dominate Page Weight
According to theHTTP Archive Web Almanac, images account for a median of 906KB of the total page weight on mobile — more than JavaScript, CSS, and HTML combined. On image-heavy sites like e-commerce and blogs, that figure climbs to 2–4MB per page.
Google's Core Web Vitals include Largest Contentful Paint (LCP) — the time until the largest image or text block on the page is fully rendered.Google considers LCP under 2.5 seconds as “Good.”An uncompressed 3MB hero image on a 4G mobile connection takes 4–8 seconds alone to load. That is a failing LCP score from a single asset.
Step 1: Resize to the Display Dimensions
The first fix is always dimensions. Never serve a 4000px-wide image to a 400px mobile viewport. The browser downloads all 4000 pixels, then throws away 90% of them.
For responsive sites you need multiple sizes. Here is how to generate them with Sharp in Node.js:
import sharp from 'sharp';
const BREAKPOINTS = [400, 800, 1200, 1600];
for (const width of BREAKPOINTS) {
await sharp('hero-original.jpg')
.resize({ width, withoutEnlargement: true })
.webp({ quality: 80 })
.toFile(`hero-${width}w.webp`);
}
// Also generate AVIF for browsers that support it
for (const width of BREAKPOINTS) {
await sharp('hero-original.jpg')
.resize({ width, withoutEnlargement: true })
.avif({ quality: 75, effort: 4 })
.toFile(`hero-${width}w.avif`);
}Step 2: Convert to Modern Formats
JPEG and PNG were designed in the 1990s. WebP (2010) and AVIF (2019) use fundamentally better compression algorithms. At the same visual quality:
| Format | Size vs JPEG | Browser Support | Best For |
|---|---|---|---|
| JPEG | Baseline | 100% | Fallback only |
| PNG | 2–5× larger | 100% | Transparency fallback (use WebP instead) |
| WebP | 30% smaller | 97% | Photos, screenshots, UI elements |
| AVIF ★ | 50% smaller | 93% | Hero images, blog photos, product shots |
| SVG | Infinitely scalable | 100% | Icons, logos, illustrations |
Step 3: Implement Responsive Images with srcset
The srcset attribute tells the browser which image version to download based on the viewport width. The browser picks the smallest image that still renders sharply — it does this math itself, you just provide the options:
<picture>
<!-- AVIF for modern browsers -->
<source
type="image/avif"
srcset="hero-400w.avif 400w,
hero-800w.avif 800w,
hero-1200w.avif 1200w,
hero-1600w.avif 1600w"
sizes="(max-width: 600px) 100vw,
(max-width: 1200px) 80vw,
1200px"
/>
<!-- WebP fallback -->
<source
type="image/webp"
srcset="hero-400w.webp 400w,
hero-800w.webp 800w,
hero-1200w.webp 1200w,
hero-1600w.webp 1600w"
sizes="(max-width: 600px) 100vw,
(max-width: 1200px) 80vw,
1200px"
/>
<!-- JPEG fallback for old browsers -->
<img
src="hero-800w.jpg"
alt="Screenshot of FileMint image compressor interface showing AVIF output"
width="1200"
height="630"
loading="eager"
fetchpriority="high"
/>
</picture>Note fetchpriority="high"on the LCP image. This tells the browser to prioritise this image's download above other resources. For the hero image above the fold, this is the most direct way to improve your LCP score.
Step 4: Lazy Load Below-the-Fold Images
Images below the fold should not block the initial page load. The loading="lazy" attribute defers download until the user scrolls near the image. Browser support is 96% globally.
<!-- Hero image: NEVER lazy load — use eager + fetchpriority -->
<img src="hero.webp" loading="eager" fetchpriority="high" alt="..." />
<!-- All other images: lazy load to defer download -->
<img src="product-thumbnail.webp" loading="lazy" alt="..." />
<!-- For older browsers, use IntersectionObserver as a polyfill -->
const images = document.querySelectorAll('img[loading="lazy"]');
if ('loading' in HTMLImageElement.prototype === false) {
// Fallback for Safari < 15.4 and IE 11
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target as HTMLImageElement;
img.src = img.dataset.src ?? '';
observer.unobserve(img);
}
});
});
images.forEach(img => observer.observe(img));
}Common Mistakes That Undermine All the Above
CSS Resizing Is Not Image Resizing
Setting max-width: 100% in CSS scales the display size. It does not change the file the browser downloads. A 3MB image withmax-width: 100% is still 3MB. Always resize the file on disk (or at build time), not just in CSS.
Lazy Loading the LCP Image
This is the most impactful single mistake. If you put loading="lazy"on the hero image — the first image the user sees — the browser deliberately delays downloading it. Your LCP score takes the penalty of that delay directly. Check which image triggers your LCP in Chrome DevTools > Performance > Timings. Make sure that specific image has loading="eager" andfetchpriority="high".
Missing or Generic Alt Text
Alt text is not just accessibility — it is a ranking signal. An image withalt="" contributes nothing to Google Image Search. An image with alt="Screenshot of Chrome DevTools Performance panel showing LCP metric"ranks for those terms. Be specific. Describe what is actually in the image.
Missing Width and Height Attributes
Without explicit width and heightattributes, the browser does not know the image's aspect ratio until it downloads the file. This causes Cumulative Layout Shift (CLS) — the page reflows as images load in, pushing content around. CLS is another Core Web Vitals signal that affects rankings.
Fix These in Priority Order
- 1Add width + height attributes to every img tag. Eliminates CLS. Zero seconds of effort per image.
- 2Convert hero images to WebP at quality 80. Typical 50–70% file size reduction. Biggest LCP win.
- 3Add loading="lazy" to all below-fold images. Defers download until needed.
- 4Add srcset with multiple sizes. Eliminates oversized image downloads on mobile.
- 5Upgrade to AVIF + WebP fallback once the WebP pipeline is stable. Additional 20–30% size reduction.
For the format decision in detail — when AVIF beats WebP and when it does not — read our AVIF vs WebP comparison. For understanding the compression quality tradeoffs between lossy and lossless modes, our image compression guide covers the SSIM quality metrics in detail.
Compress images to WebP or AVIF — locally.
Drop any image and download a compressed version. No upload. No server. Works offline.
Open Image Compressor →