Performance Optimization

Master image performance optimization. Learn about lazy loading, preloading, preventing layout shift, and improving Core Web Vitals.

Core Web Vitals

Images significantly impact Core Web Vitals, Google's key metrics for user experience. Proper image optimization directly improves your SEO ranking.

LCP

Largest Contentful Paint

Measures loading performance. Images are often the LCP element. Target: < 2.5 seconds.

CLS

Cumulative Layout Shift

Measures visual stability. Images without dimensions cause layout shifts. Target: < 0.1.

FID/INP

Interaction Responsiveness

Measures interactivity. Large images can block the main thread during decode.

Lazy Loading

Lazy loading defers the loading of off-screen images until the user scrolls near them. This improves initial page load time and saves bandwidth.

<!-- Native lazy loading (recommended) -->
<img
  src="https://cdn.optimage.com/col_abc123/768/image.webp"
  srcset="..."
  sizes="100vw"
  alt="Description"
  loading="lazy"
  decoding="async"
/>

When to Use Lazy Loading

  • Images below the fold (not visible on initial load)
  • Image galleries and carousels
  • Product listings and grids
  • Blog post content images

When NOT to Lazy Load

  • Hero images and banners (above the fold)
  • LCP (Largest Contentful Paint) elements
  • Images visible in the initial viewport

Preloading Critical Images

For above-the-fold images (especially LCP elements), preload them to start downloading early:

<!-- In <head> section -->
<link
  rel="preload"
  as="image"
  href="https://cdn.optimage.com/col_hero/1280/banner.webp"
  imagesrcset="
    https://cdn.optimage.com/col_hero/320/banner.webp 320w,
    https://cdn.optimage.com/col_hero/768/banner.webp 768w,
    https://cdn.optimage.com/col_hero/1280/banner.webp 1280w
  "
  imagesizes="100vw"
/>

Using fetchpriority

The fetchpriority attribute tells the browser how important an image is relative to other resources:

<!-- High priority for LCP image -->
<img
  src="https://cdn.optimage.com/col_hero/768/banner.webp"
  srcset="..."
  sizes="100vw"
  alt="Hero banner"
  loading="eager"
  fetchpriority="high"
  decoding="sync"
/>

<!-- Low priority for decorative images -->
<img
  src="https://cdn.optimage.com/col_bg/768/pattern.webp"
  alt=""
  loading="lazy"
  fetchpriority="low"
  decoding="async"
/>

Preventing Layout Shift (CLS)

Layout shift occurs when images load without reserved space, pushing content around. Always reserve space for images to achieve a good CLS score.

Method 1: Width and Height Attributes

<!-- Browser calculates aspect ratio from width/height -->
<img
  src="https://cdn.optimage.com/col_abc123/768/photo.webp"
  width="1600"
  height="900"
  alt="Photo"
  class="w-full h-auto"
/>

Method 2: CSS aspect-ratio

<!-- Using CSS aspect-ratio property -->
<img
  src="https://cdn.optimage.com/col_abc123/768/photo.webp"
  alt="Photo"
  class="w-full"
  style="aspect-ratio: 16 / 9;"
/>

<!-- Or with Tailwind CSS -->
<img
  src="..."
  alt="Photo"
  class="w-full aspect-video object-cover"
/>

Method 3: Container with Padding

<!-- Legacy method (still works everywhere) -->
<div class="relative" style="padding-bottom: 56.25%;">
  <img
    src="..."
    alt="Photo"
    class="absolute inset-0 w-full h-full object-cover"
  />
</div>

Tip: Modern browsers automatically calculate aspect ratio from width/height attributes, even with width: 100% CSS. This is the simplest and most reliable method.

Image Decoding

The decoding attribute controls how image decoding affects the main thread:

ValueBehaviorUse Case
syncDecode immediately, may blockCritical LCP images
asyncDecode in backgroundMost images (default)
autoBrowser decidesWhen unsure
<!-- Critical image: decode synchronously -->
<img src="hero.webp" decoding="sync" loading="eager" />

<!-- Non-critical image: decode asynchronously -->
<img src="gallery-item.webp" decoding="async" loading="lazy" />

CDN and Caching

OPTIMAGE serves all images from Cloudflare's global CDN with optimal caching:

  • Global edge locations: Images served from the nearest data center
  • HTTP/2 and HTTP/3: Faster multiplexed connections
  • Long cache headers: Images cached in browser and CDN edge
  • Immutable URLs: Each image has a unique, cacheable URL

Cache Headers

Cache-Control: public, max-age=31536000, immutable
Content-Type: image/webp

Note: OPTIMAGE images are immutable—the content at a URL never changes. If you need to update an image, upload a new one with a different slug.

Optimization Checklist

Use this checklist to ensure your images are fully optimized:

For All Images

  • Use WebP format (automatic with OPTIMAGE)
  • Include srcset with appropriate sizes
  • Set sizes attribute matching your layout
  • Add width and height or aspect-ratio
  • Include descriptive alt text

For Above-the-Fold Images

  • Set loading="eager"
  • Set fetchpriority="high" for LCP
  • Consider decoding="sync" for hero images
  • Add <link rel="preload"> in head

For Below-the-Fold Images

  • Set loading="lazy"
  • Set decoding="async"
  • Consider fetchpriority="low" for decorative images

Example: Fully Optimized Page

<!DOCTYPE html>
<html>
<head>
  <!-- Preload LCP image -->
  <link
    rel="preload"
    as="image"
    href="https://cdn.optimage.com/col_hero/1280/banner.webp"
    imagesrcset="
      https://cdn.optimage.com/col_hero/768/banner.webp 768w,
      https://cdn.optimage.com/col_hero/1280/banner.webp 1280w
    "
    imagesizes="100vw"
  />
</head>
<body>
  <!-- Hero (LCP element) -->
  <img
    src="https://cdn.optimage.com/col_hero/768/banner.webp"
    srcset="..."
    sizes="100vw"
    width="1920"
    height="1080"
    alt="Welcome to our site"
    loading="eager"
    fetchpriority="high"
    decoding="sync"
    class="w-full h-auto"
  />

  <!-- Product grid -->
  <div class="grid grid-cols-3 gap-4">
    <img
      src="https://cdn.optimage.com/col_products/768/product-1.webp"
      srcset="..."
      sizes="33vw"
      width="800"
      height="800"
      alt="Product 1"
      loading="lazy"
      decoding="async"
      class="w-full aspect-square object-cover"
    />
    <!-- More products... -->
  </div>
</body>
</html>