Strategies for Optimizing Images for Better Web Performance

6 min read
📝 794 words
Strategies for Optimizing Images for Better Web Performance

Strategies for Optimizing Images for Better Web Performance

Images are often the largest files on a web page and can significantly impact load times and overall performance. Here are some strategies to optimize images for better web performance:

Why Images Slow Down Websites

  • File Size Overhead - Large JPEGs or PNGs can weigh several MBs each.
  • Wrong Formats - Using PNGs where WebP or AVIF would have cut the size in half.
  • No Responsive Variants - Loading a 2000px image on a 400px mobile screen.
  • Eager Loading - Forcing all images to load at once, even those below the fold.

Each of these adds unnecessary weight, slows down rendering, and creates a poor user experience.

Measuring Image Performance Impact

You can use tools like Lighthouse, WebPageTest, or GTmetrix to analyze your website's performance and identify image-related issues. Look for metrics like Largest Contentful Paint (LCP) and Total Blocking Time (TBT) to see how images affect load times.

Image Optimization Strategies

(summary of techniques, details below)

  1. Modern Formats: Use modern image formats like WebP or AVIF for better compression and quality.
  2. Responsive Images: Serve different image sizes based on the user's device using `src
  3. Lazy Loading: Implement lazy loading for offscreen images to reduce initial load time.
  4. Compression: Use tools like ImageOptim or TinyPNG to compress images without losing quality.
  5. CDN: Serve images from a Content Delivery Network (CDN) to reduce latency.
  6. Image Dimensions: Always specify width and height attributes to prevent layout shifts.
  7. SVG Optimization: Convert SVGs to inline SVGs and optimize them using tools like SVGO.
  8. Use CSS Sprites: Combine multiple images into a single sprite sheet to reduce HTTP requests.
  9. Cache Control: Set appropriate cache headers to leverage browser caching for images.
  10. Preload Key Images: Use the preload attribute for critical images to ensure they load quickly.

1. Switching to Modern Formats (WebP / AVIF)

Use modern formats like WebP or AVIF for better compression and quality. Reserve JPEG and PNG for legacy support.

WebP offers 25-35% smaller sizes than JPEG/PNG without noticeable quality loss. AVIF compresses even better, though with slower encoding.

Example (Next.js next/image automatically serves WebP/AVIF when supported):

import Image from "next/image";

export default function Hero() {
  return (
    <Image
      src="/assets/images/balloons.jpg"
      alt="Colorful Balloons"
      width={1200}
      height={600}
      priority
    />
  );
}

2. Using Responsive Images and the dimensions i rendered (srcset)

Use the srcset attribute to serve different image sizes based on the user's device.

Instead of serving one giant image, you can generated multiple sizes and let the browser choose the best one.

<img 
  src="balloons-800.webp" 
  srcset="balloons-400.webp 400w, balloons-800.webp 800w, balloons-1200.webp 1200w" 
  sizes="(max-width: 600px) 400px, (max-width: 1200px) 800px, 1200px"
  alt="Colorful Balloons"
/>

This prevents a mobile device from wasting bandwidth on desktop-sized images.

3. Converting small Images to Base64

This has been the best hack for fixing the LCP which I highly recommend, convert the images to the required dimensions in webp format, and then converted this images to Base64 string, which I injected in the img code directly.

 {/* 
    Before 
 <Image
    src={"/images/tree.png"} // instead use base64 
    alt={`Image is a tree`}
    width={150}
    priority
    id="tree-image"
    height={150}
    aria-label="Image is a tree"
  /> */}

<Image
    src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAYAAAA+s"
    alt={`Image is a tree`}
    width={150}
    id="tree-image"
    height={150}
    aria-label="Image is a tree"
/>

You can use a tool like Image to Base64 to convert the image to base64 and then copy the string it generates and paste in the src of the Image element.

3. Lazy Loading Images Below the Fold

Lazy loading defers non-critical images until they're about to enter the viewport.

<img src="gallery-1.webp" loading="lazy" alt="Gallery item" />

In React/Next.js, next/image has loading="lazy" by default.

 import Image from 'next/image';

 <Image
    loading="lazy" // add this
    fetchPriority="low" // add this
    src={optimizedAvatarUrl.toString()}
    alt={avatarAlt}
    width={width}
    height={height}
    className="rounded-full object-cover"
    priority
  />

4. Compressing Aggressively

  • TinyPNG / TinyJPG for quick manual compression.
  • Sharp for automated build-time optimization.
  • Cloudflare Images or Imgix for on-the-fly CDN-based compression.

5. Serving via a CDN

Instead of pulling images directly from my server, I cached and delivered them via a CDN. This reduced latency and improved global performance.

Image Optimization Tools

  • Squoosh - Google's free browser-based converter.
  • Sharp - Node.js image processing library for automation.
  • iLoveImg - Free online image compressor and converter.
  • Image to Base64 - Free online tool to convert images to Base64 strings.
  • Cloudflare Images - CDN with built-in image optimization.
  • Imgix - Real-time image processing and CDN.
  • ImageOptim - Mac app for lossless image compression.
  • TinyPNG / TinyJPG - Online tool for compressing PNG and JPEG images.
  • SVGO - Node.js-based tool for optimizing SVG files.