Skip to main content
Beta: Front-End Checklist is currently in beta. Some issues are still being fixed. Thanks for your patience.
ImagesHigh

Keep image file sizes within recommended limits

Individual image files are compressed to reasonable sizes to avoid wasted bandwidth and slow load times, especially on mobile networks.

Utilities
Quick take
Typical fix time 20 min
  • Photos: target under 200KB (WebP 80% quality); under 400KB for full-bleed hero images
  • Graphics/icons: target under 50KB; use SVG for vector graphics
  • WebP is typically 25-35% smaller than equivalent-quality JPEG (per web.dev)
  • AVIF is typically 40-50% smaller than equivalent-quality JPEG (per web.dev)
Why it matters: Images account for over 50% of average page weight according to HTTP Archive data. Oversized images waste user bandwidth (costly on mobile data plans), delay Largest Contentful Paint (LCP), and increase bounce rates. A 1-second delay in mobile page load can reduce conversions by up to 20% according to Google research.

Rule Details

Image file size is the biggest single contributor to page weight. Keeping file sizes in check directly improves load time, LCP, and bandwidth costs.

Code Example

# Using Sharp (Node.js) — recommended settings
 
# WebP (photos)
sharp('input.jpg').webp({ quality: 80 }).toFile('output.webp')
 
# AVIF (photos, highest compression)
sharp('input.jpg').avif({ quality: 60, effort: 6 }).toFile('output.avif')
 
# JPEG (legacy fallback)
sharp('input.jpg').jpeg({ quality: 80, progressive: true, mozjpeg: true }).toFile('output.jpg')
 
# PNG (lossless with metadata stripping)
sharp('input.png').png({ compressionLevel: 9, effort: 10 }).toFile('output.png')

Why It Matters

Images account for over 50% of average page weight according to HTTP Archive data. Oversized images waste user bandwidth (costly on mobile data plans), delay Largest Contentful Paint (LCP), and increase bounce rates. A 1-second delay in mobile page load can reduce conversions by up to 20% according to Google research.

Image TypeMax Recommended SizeFormat
Hero / full-bleed banner400KBWebP or AVIF
Content photo (article, blog)200KBWebP or AVIF
Product image (e-commerce)150KBWebP or AVIF
Thumbnail30KBWebP or AVIF
Icon / logo10KBSVG or WebP
PNG with transparency100KBWebP (with alpha) or PNG

These are targets, not hard limits—an image serving 40% of users at 1600px width may reasonably exceed 200KB if optimised correctly.

Format Compression Comparison

According to web.dev (opens in new tab):

  • WebP produces files ~25-35% smaller than equivalent-quality JPEG
  • AVIF produces files ~40-50% smaller than equivalent-quality JPEG
<!-- Use <picture> to serve the smallest supported format -->
<picture>
  <source
    type="image/avif"
    srcset="photo-400.avif 400w, photo-800.avif 800w"
    sizes="(max-width: 600px) 100vw, 50vw"
  >
  <source
    type="image/webp"
    srcset="photo-400.webp 400w, photo-800.webp 800w"
    sizes="(max-width: 600px) 100vw, 50vw"
  >
  <img
    src="photo-800.jpg"
    srcset="photo-400.jpg 400w, photo-800.jpg 800w"
    sizes="(max-width: 600px) 100vw, 50vw"
    alt="Photo description"
    width="800"
    height="600"
    loading="lazy"
  >
</picture>

Batch Optimisation Script

// scripts/optimise-images.mjs
import sharp from 'sharp'
import { globSync } from 'glob'
import { statSync } from 'fs'
import path from 'path'
 
const THRESHOLDS = {
  '.jpg': 200 * 1024,   // 200KB
  '.jpeg': 200 * 1024,
  '.png': 100 * 1024,
  '.webp': 200 * 1024,
}
 
const images = globSync('public/images/**/*.{jpg,jpeg,png,webp}')
 
for (const imgPath of images) {
  const ext = path.extname(imgPath).toLowerCase()
  const threshold = THRESHOLDS[ext]
  const size = statSync(imgPath).size
 
  if (size > threshold) {
    console.warn(`Oversized: ${imgPath} (${Math.round(size / 1024)}KB, threshold ${Math.round(threshold / 1024)}KB)`)
 
    // Generate WebP version
    const webpPath = imgPath.replace(ext, '.webp')
    await sharp(imgPath)
      .webp({ quality: 80 })
      .toFile(webpPath)
 
    const webpSize = statSync(webpPath).size
    const saving = Math.round((1 - webpSize / size) * 100)
    console.log(`  → WebP: ${Math.round(webpSize / 1024)}KB (${saving}% smaller)`)
  }
}

Lighthouse CI Integration

// lighthouserc.js
module.exports = {
  ci: {
    assert: {
      assertions: {
        // Fail if any image could be reduced by more than 4KB
        'uses-optimized-images': ['error', { minScore: 0.9 }],
        // Fail if any image is significantly larger than its display size
        'uses-responsive-images': ['error', { minScore: 0.9 }],
      }
    }
  }
}

Stripping EXIF Metadata

Camera images embed EXIF metadata (GPS location, camera model, timestamps) that adds unnecessary bytes. Strip it before serving.

// Sharp strips metadata by default when converting format
// For JPEG-to-JPEG, use withMetadata(false)
await sharp('input.jpg')
  .jpeg({ quality: 80 })
  // metadata is stripped by default; use .withMetadata() to preserve it
  .toFile('output.jpg')

Testing

  1. Open Chrome DevTools → Network tab → filter by Img → check Transfer Size column
  2. Run Lighthouse — look for "Properly size images" and "Efficiently encode images" opportunities
  3. Use WebPageTest waterfall view to identify large image requests
  4. Run the batch optimisation script above in CI to flag regressions

Verification

Automated Checks

  • Compare before/after transfer sizes in DevTools or your CDN logs to ensure the optimised asset is actually being served in production.
  • Review a few images visually on real devices to avoid introducing visible artefacts while chasing byte savings.

Manual Checks

  • Confirm photos stay under roughly 200KB, hero images under 400KB, and thumbnails under 30KB unless there is a documented exception.
  • Re-check LCP after compression work because the largest image often dominates the metric.

Use with AI

Copy these prompts to use with your AI assistant, or install the MCP server to use directly from Claude, Cursor, or Windsurf.

Check

Verify implementation

Audit all image assets in this project. For each image: 1) Check the file size in bytes. 2) Flag any photo (JPEG/WebP/AVIF) over 200KB. 3) Flag any graphic/icon (PNG/SVG) over 100KB. 4) Flag any hero or full-width image over 400KB. 5) Flag any thumbnail (under 200px wide) over 30KB. Also check if Lighthouse reports 'Properly size images' or 'Efficiently encode images' as opportunities. Report each oversized image with its path, current size, and an estimated target size.

Fix

Auto-fix issues

For each oversized image: 1) Convert JPEG/PNG to WebP at 80% quality using Squoosh, Sharp, or ImageOptim—this typically yields 25-35% size reduction over optimised JPEG. 2) Try AVIF at 60% quality for further reduction (often 40-50% smaller than JPEG). 3) Strip metadata (EXIF/IPTC) from images served on the web. 4) For PNGs, run through oxipng or pngquant for lossless or near-lossless compression. 5) For SVGs, run through SVGO. 6) Implement multiple srcset sizes so mobile users download smaller files.

Explain

Learn more

Explain how oversized images impact web performance. Images are typically the largest assets on a page—HTTP Archive data consistently shows images account for 40-60% of total page weight. Each KB saved reduces transfer time, especially on mobile networks (4G averages 20Mbps, with significant latency). Large images also delay Largest Contentful Paint (LCP), the primary Core Web Vitals metric. Google's Lighthouse flags images that could be reduced by more than 4KB as an opportunity.

Review

Code review

Inspect image assets, build transforms, CMS output, and rendered markup. Flag photos above the documented file-size thresholds, hero images that exceed budget without justification, and image pipelines that ship original uploads instead of compressed responsive variants.

Sources

References used to support the guidance in this rule.

Further Reading

Tools and supplementary material for exploring the topic in more depth.

Image performance — web.dev

by web.dev

web.devArticle
Use WebP images — web.dev

by web.dev

web.devArticle
Decrease front-end size — web.dev

by web.dev

web.devArticle
Squooshsquoosh.appTool
ImageOptimimageoptim.comTool
Sharpsharp.pixelplumbing.comTool
Lighthousedeveloper.chrome.comTool
WebPageTestwebpagetest.orgTool

Rules that often go hand-in-hand with this one.

Use modern image formats (WebP, AVIF)

Images are served in modern formats (WebP or AVIF) instead of legacy JPEG/PNG where browser support allows, reducing file size without visible quality loss.

Images
Serve images at the correct display size

Images are not significantly larger than their display dimensions—serving a 2000px image for a 400px container wastes bandwidth and hurts LCP.

Images
Optimise images for faster loading

All images are compressed and metadata-stripped before deployment, removing unnecessary bytes without visible quality loss.

Images
Set explicit width and height on images

All <img> elements have explicit width and height attributes so browsers can reserve space before the image loads, preventing layout shift.

Images

Was this rule helpful?

Your feedback helps improve rule quality. This stays internal for now.

Loading feedback...
0 / 385