Optimizing Images for Performance: Best Practices for Web
By Mason Goulding · · Updated
Images are the heaviest files most sites ship. Get them right and your pages feel instant. Get them wrong and users bounce before the hero even paints.
On modern networks, how you serve images often matters more than the images themselves. Formats, sizing, compression, loading strategy, and caching all decide whether your LCP (Largest Contentful Paint) lands under two and a half seconds—or drifts into “close tab” territory. Google’s guidance for fast loading starts with media discipline; see the high-level playbook at web.dev/fast. This article is your practical, developer-first checklist to get there without sandblasting visual quality.
If you’re new to Core Web Vitals, start with Understanding Core Web Vitals for why LCP/INP/CLS rise and fall with image decisions.
1) Choose the Right Format (WebP, AVIF, SVG, PNG, JPEG)
Pick formats intentionally. AVIF delivers the smallest files for photographic content at a given quality in most cases, with excellent detail retention; WebP is widely supported and often close behind. Use SVG for logos, icons, and line illustrations—infinitely scalable, tiny, and crisp. Reserve PNG for true transparency or pixel-art; stick with JPEG only when you need maximum compatibility with legacy systems.
A simple strategy: attempt AVIF first, fall back to WebP, then JPEG/PNG. You can implement this with the <picture>
element so browsers pick the best they support. For background and rationale, see web.dev — Optimize your images.
<picture>
<source type="image/avif" srcset="/img/hero@1x.avif 1x, /img/hero@2x.avif 2x">
<source type="image/webp" srcset="/img/hero@1x.webp 1x, /img/hero@2x.webp 2x">
<img src="/img/hero@1x.jpg" width="1200" height="630" alt="Product in use outdoors">
</picture>
2) Serve Only the Pixels Each Device Needs
Responsive images prevent a 390-px mobile viewport from downloading a 2400-px desktop asset. Use srcset
with width descriptors and a realistic sizes
attribute so the browser chooses the smallest adequate file. MDN’s guide is the canonical reference: Responsive images.
<img
src="/img/card-800.webp"
srcset="/img/card-400.webp 400w, /img/card-800.webp 800w, /img/card-1200.webp 1200w"
sizes="(max-width: 640px) 90vw, (max-width: 1024px) 50vw, 400px"
width="400" height="300" alt="Close-up of handcrafted detail">
Don’t guess your sizes
. Inspect your layout at breakpoints (devtools device emulation) and plug real values. For layout strategy across breakpoints, pair this with How to Handle Responsive Breakpoints.
3) Compress Aggressively (Without Looking Crunchy)
Modern codecs tolerate much lower bitrates than you think. Start around q=45–60
for WebP and a similar perceived setting for AVIF, then A/B on real devices. Use perceptual metrics (SSIM/DSSIM) if you batch offline, but the final arbiter is the human eye on a phone screen. Re-compress legacy JPEG/PNG—transcoding alone can cut 30–60%.
When you can’t change your pipeline, defer to an edge optimizer that rewrites and caches images near the user. Cloud providers explain the wins and tradeoffs clearly; see Cloudflare — What is image optimization?.
4) Always Declare Dimensions (Stop CLS at the Source)
Cumulative Layout Shift (CLS) often comes from images without reserved space. Always set width
and height
(or CSS aspect-ratio
) so the browser can allocate space before the file downloads. That preserves layout stability and trust.
<img
src="/img/feature-1200.webp"
width="1200" height="800"
style="aspect-ratio: 3 / 2;"
alt="Feature highlight on dashboard">
5) Lazy-Load Below the Fold (But Eager-Load Your LCP)
Use native loading="lazy"
for non-critical images to prevent wasted bandwidth and main-thread work. Do not lazy-load your LCP hero; ship it eager and preconnect/preload as needed so it paints fast. For a deeper comparison of strategies, see Lazy Loading vs. Eager Loading.
<img src="/img/gallery-2-600.webp" width="600" height="400" loading="lazy" alt="Customer gallery item">
6) Use <picture>
for Art Direction
Sometimes the “same image, new size” rule isn’t enough. Cropping a portrait tighter on mobile can preserve meaning—and slash bytes. The <picture>
element lets you swap sources and crops at breakpoints while keeping alt text and semantics consistent.
<picture>
<source media="(max-width: 640px)" srcset="/img/hero-mobile.avif">
<source media="(min-width: 641px)" srcset="/img/hero-desktop.avif">
<img src="/img/hero-desktop.webp" width="1200" height="630" alt="Team collaborating in studio">
</picture>
7) Prefer SVG for Icons and UI Glyphs
Ship icons as inline SVG (or a small sprite) instead of icon fonts. SVGs are crisp at any pixel density, styleable with CSS, and tiny when optimized. Remove metadata, collapse groups, and simplify paths in your build step.
8) Deliver from the Edge (Caching, Resizing, Next-Gen at the CDN)
Even perfect files feel slow when they travel far. Put images on a CDN, set long cache lifetimes for versioned URLs, and let the edge resize and transcode per request. That avoids shipping a 2× retina asset to a 1× device 6,000 miles away.
If you need a refresher on CDNs and why latency dominates user perception, start with PageSpeed Insights to surface network and caching issues in one pass.
9) SEO & Accessibility: Alt Text, Indexation, and Thumbnails
Performance and SEO travel together. Use descriptive alt
text for non-decorative images, lazy-load responsibly, and make sure critical images aren’t blocked by robots rules. Google’s image guidance covers filenames, dimensions, and more; see Image best practices.
When you’re aligning performance with crawlability and structure, see Technical SEO for Hand-Coded Sites.
10) Automate the Boring Parts (Pipelines that Never Forget)
Hand-optimizing a few assets is fine; hand-optimizing forever is not. Bake optimization into CI: generate AVIF/WebP variants, resize responsive breakpoints, apply quality presets, strip metadata, and fail builds when assets exceed budgets. Keep originals in source control; publish hashed, versioned outputs to your CDN.
For a practical pre-launch workflow that includes image gates alongside accessibility and performance checks, use Checklist Before Launching a Site.
11) Test on Real Devices, Throttle Like It’s 3G
Lab tools are great, but the truth lives on phones. Throttle the network in devtools, test on older Android hardware, and compare results against your analytics’ real device mix. Confirm that your LCP element is an image (often the hero) and treat it as a first-class citizen: eager load, correct size, short path, CDNs close by.
Common Pitfalls (and What to Do Instead)
- Uploading 4K originals: Cap source widths to your widest container; archive originals elsewhere.
- Lazy-loading everything: Eager-load your LCP and above-the-fold media; lazy-load the rest.
- Forgetting dimensions: Always set
width
/height
oraspect-ratio
to prevent CLS. - One-size-fits-all quality: Product close-ups can compress harder than gradients and text overlays—tune per use case.
- Icon fonts: Ship inline SVGs; drop font-based icons unless legacy demands it.
Keep going: Lazy Loading vs. Eager Loading · How to Handle Responsive Breakpoints · Technical SEO for Hand-Coded Sites · Checklist Before Launching a Site · Understanding Core Web Vitals
Authoritative references (for deeper dives):