Performance

Image Optimization for Web Performance: A Practical Checklist

Images are the heaviest thing on the average web page — roughly half of total page weight. They are also the most common Largest Contentful Paint (LCP) element, which means they directly move the Core Web Vitals numbers Google ranks you on. Here is the checklist we use.

1. Serve the right format

Photos as JPG or WebP, graphics as PNG or WebP, and never ship a GIF that isn't animating. If you can only do one thing from this article, convert your heaviest photos to WebP — it's a ~25% saving with universal browser support. (Full comparison: PNG vs JPG vs WebP.)

2. Compress to the visual sweet spot

Quality 80 for photos is the classic default; push lower with a before/after preview until you see artifacts. A hero image that arrives at 180 KB instead of 1.4 MB is the cheapest LCP win available. Our guide to compressing without visible loss covers the technique.

3. Size images to their container

Shipping a 4000px camera photo into a 800px column wastes ~90% of the bytes. Resize to the largest size you'll display, then let srcset serve smaller variants to smaller screens:

<img srcset="hero-800.webp 800w, hero-1600.webp 1600w" sizes="(max-width: 800px) 100vw, 800px">

4. Lazy-load below the fold — and only below

Native loading="lazy" keeps offscreen images from competing for bandwidth. But never lazy-load your LCP image — that delays the render Google is timing. The hero gets fetchpriority="high"; everything below the fold gets loading="lazy".

5. Reserve space to kill layout shift

Always set width and height (or CSS aspect-ratio) so the browser reserves the box before the image arrives. This is the difference between a good and bad Cumulative Layout Shift score.

6. Cache and CDN

Images rarely change: serve them with a long Cache-Control lifetime and fingerprinted filenames. A CDN puts them geographically close to users — most static hosts (GitHub Pages, Netlify, Cloudflare) do this for free.

The checklist

  • Right format per image (photo → JPG/WebP, graphics → PNG/WebP)
  • Compressed with a visual check, not a blind default
  • Resized to display dimensions, with srcset variants
  • loading="lazy" below the fold, fetchpriority="high" on the LCP image
  • Explicit dimensions on every <img>
  • Long cache lifetimes, served from a CDN

The first two steps happen before the image ever reaches your repo — and they're exactly what FreeCompressor does in your browser, for free, without uploading a single byte.