ImgForge

Image Optimization for Web Performance: A Developer Guide

By ImgForge Team — Published January 7, 2026

Images account for the largest share of page weight on the web — often more than all JavaScript and CSS combined. Getting image optimization right is one of the highest-return investments you can make in web performance, directly improving Core Web Vitals scores, user experience, and search rankings.

Why Images Drive Core Web Vitals

Google's Core Web Vitals are the primary performance signals used in search ranking. Images directly affect two of the three metrics: Largest Contentful Paint (LCP) and Cumulative Layout Shift (CLS). Understanding how images influence each metric is the first step toward fixing them.

Largest Contentful Paint (LCP)

LCP measures how long it takes for the largest visible content element to render. On most pages, that element is an image — typically a hero image, product photo, or above-the-fold banner. A good LCP score is under 2.5 seconds. To hit this target, the hero image must be small enough to download quickly, use a modern format, be served from a nearby CDN edge node, and ideally be preloaded. Use loading="eager" and fetchpriority="high" on your LCP image. Do not lazy-load it.

Cumulative Layout Shift (CLS)

CLS measures unexpected layout shifts — when elements jump around as the page loads. Images without explicit width and height attributes cause the browser to allocate no space for them initially, so when they load, the surrounding content reflows. The fix is always to include width and height on every img element, or use CSS aspect-ratio to reserve space. This lets the browser calculate the image's dimensions before the file downloads.

Choosing the Right Format

Format selection is the single biggest lever for reducing image file size. The right choice depends on the image content and required browser support:

  • WebP — 25-35% smaller than JPG at equivalent quality. Supported by all modern browsers. The safe default for photographs and complex images when you need broad compatibility.
  • AVIF — 50% smaller than JPG in many cases. Excellent for photographs with HDR or wide color gamut content. Browser support is now over 90% globally but verify your target audience before deploying exclusively.
  • JPG — The universal fallback. Use quality 75-85 for photographs. Still the right choice when you need guaranteed compatibility with legacy browsers or email clients.

Use the picture element to serve modern formats with a JPG fallback, so every user gets the best format their browser supports:

<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="630">
</picture>

Responsive Images with srcset

Serving a 2400px-wide image to a mobile user wasting 80% of the downloaded pixels. The srcset attribute lets the browser choose the most appropriate image size based on the device's viewport and pixel density:

<img
  src="photo-800.jpg"
  srcset="photo-400.jpg 400w,
          photo-800.jpg 800w,
          photo-1200.jpg 1200w,
          photo-2400.jpg 2400w"
  sizes="(max-width: 640px) 100vw,
         (max-width: 1024px) 50vw,
         800px"
  alt="Responsive photo"
  width="800"
  height="600"
>

The sizes attribute tells the browser how wide the image will be rendered at different viewport widths, before it downloads the image. Getting this right ensures the browser picks the smallest image that still looks sharp. Generate your srcset variants at 400w, 800w, 1200w, and 2400w to cover all common screen densities.

Lazy Loading

Native lazy loading defers off-screen images until the user scrolls near them, reducing initial page weight with a single HTML attribute:

<img src="photo.webp" alt="Below fold image" loading="lazy" width="800" height="600">

Never lazy-load above-the-fold images or your LCP candidate. Apply loading="lazy" only to images that start below the visible viewport. For the LCP image, use loading="eager" (the default) and add fetchpriority="high" to tell the browser to prioritize it above other resources.

Compression Best Practices

Format choice and compression quality together determine file size. These settings deliver good results across most use cases:

  • WebP: quality 80-85 for photographs, lossless mode for screenshots and UI graphics with flat color areas
  • AVIF: quality 60-70 (AVIF's scale differs from JPG — lower numbers still look excellent)
  • JPG: quality 75-85 for photographs; drop to 60 for thumbnails where small file size matters more than sharpness
  • PNG: run through a lossless optimizer like oxipng or pngcrush before deploying; savings of 20-40% are common without any quality loss

CDN Delivery

Even a perfectly compressed image will load slowly if it is served from a single origin server far from the user. A Content Delivery Network (CDN) caches your images at edge nodes geographically close to your users. For images specifically, look for a CDN that provides:

  • Automatic format negotiation — serves WebP or AVIF based on the Accept header, without you maintaining multiple source files
  • On-the-fly resizing — generate srcset variants from a single master image using URL parameters, eliminating build-step complexity
  • HTTP/2 and HTTP/3 support — reduces connection overhead when loading multiple images on the same page

Tools and Build Integration

Manual optimization does not scale. Integrate image optimization into your build pipeline so every image is automatically processed at deploy time:

  • sharp (Node.js) — the fastest Node.js image processing library. Handles resizing, format conversion, and compression. Ideal for build scripts and server-side processing.
  • imagemin — a plugin-based Node.js tool with plugins for WebP, AVIF, JPG, PNG, and SVG. Pairs well with Webpack, Vite, and Rollup.
  • Squoosh CLI — Google's open-source image compression tool available as a CLI. Excellent for scripted batch conversion with fine-grained codec control.
  • ImgForge — convert and optimize images directly in the browser without installing anything. Useful for one-off conversions or when working outside your build environment.

Build-Time Automation Example

Here is a minimal Node.js script using sharp to generate WebP and AVIF variants alongside the original JPG for every image in a source directory:

import sharp from 'sharp';
import { glob } from 'glob';

const files = await glob('src/images/**/*.jpg');

for (const file of files) {
  const base = file.replace(/\.jpg$/, '');
  await sharp(file).webp({ quality: 82 }).toFile(`${base}.webp`);
  await sharp(file).avif({ quality: 65 }).toFile(`${base}.avif`);
  console.log(`Processed: ${file}`);
}

Optimization Checklist

  • Serve WebP or AVIF with a JPG fallback using the picture element
  • Add explicit width and height attributes to every img element to prevent CLS
  • Apply loading="lazy" to all below-the-fold images
  • Use srcset and sizes to serve appropriately sized images to each device
  • Preload your LCP image with a link rel="preload" tag in the document head
  • Serve images through a CDN with edge caching enabled

Convert and optimize your images now →