Skip to main content

Image optimization

Images are often the largest assets on a storefront page. Optimizing them keeps merchants' stores fast and improves the experience for their customers. Follow these practices when you render images in your theme.

Serve appropriately sized images

Don't render full-size source images. Use the img_url filter with a size so the requested image matches its display size:

{{ product.image | img_url: '600x' }}

Request only the width or height you need and let the other dimension scale proportionally. For high-density (retina) screens, use the scale parameter to serve a sharper image:

{{ product.image | img_url: '600x', scale: 2 }}

Serve responsive images with srcset

A single image size can't be optimal for every screen. Provide several widths with srcset and let the browser pick the best one for the device's viewport and pixel density. Build each candidate with img_url:

<img
src="{{ product.image | img_url: '600x' }}"
srcset="{{ product.image | img_url: '300x' }} 300w,
{{ product.image | img_url: '600x' }} 600w,
{{ product.image | img_url: '900x' }} 900w"
sizes="(max-width: 600px) 100vw, 600px"
width="{{ product.image.width }}"
height="{{ product.image.height }}"
alt="{{ product.image.alt | escape }}">

Here's what each part does:

  • src — a fallback image for browsers that don't support srcset.
  • srcset — the candidate list. Each entry is an image URL followed by a width descriptor: 300w declares that the image is 300 pixels wide. You supply several widths and let the browser choose.
  • sizes — how much space the image will occupy once laid out, so the browser can pick a candidate before it knows the image's real dimensions. Here, (max-width: 600px) 100vw, 600px means "full viewport width on screens up to 600px, otherwise a fixed 600px."
  • width and height — the image's intrinsic pixel dimensions, which reserve space and prevent layout shift (see the next section).
  • alt — the text alternative, escaped so any quotes in the text don't break the attribute.

The browser combines the srcset widths with sizes and the device's pixel density to download just one appropriately sized image.

Reserve space to prevent layout shift

When an image loads without declared dimensions, surrounding content jumps as the image appears, which hurts Cumulative Layout Shift. Set width and height on the <img> element so the browser reserves space before the image loads. The image object exposes these values:

<img
src="{{ product.image | img_url: '600x' }}"
width="{{ product.image.width }}"
height="{{ product.image.height }}"
alt="{{ product.image.alt | escape }}">

Lazy-load offscreen images

Add loading="lazy" to images that appear below the fold so the browser defers loading them until they're needed:

<img src="{{ product.image | img_url: '600x' }}" loading="lazy" alt="{{ product.image.alt | escape }}">

Don't lazy-load images that are visible when the page first renders, such as a hero banner or the main product image. Lazy-loading above-the-fold images delays your Largest Contentful Paint.

Preload the main above-the-fold image

For the largest image visible on initial load, add a preload hint in the page <head> so the browser fetches it sooner:

<link rel="preload" as="image" href="{{ product.image | img_url: '1080x' }}">

Preload only this one image — preloading too many resources competes for bandwidth and slows the page down. For the preload_tag filter and other resource hints, see Performance.