Skip to main content

Serve correctly sized images with srcset and sizes

Use srcset and sizes attributes with Shopify's image_tag Liquid filter to serve appropriately sized images for each viewport.


Without srcset and sizes, the browser has only one image to download regardless of the viewport. Mobile devices download desktop-sized images unnecessarily, wasting bandwidth and slowing LCP. Responsive images ensure that the browser downloads an appropriately sized image for the current viewport, reducing initial page weight and improving performance.


The srcset attribute provides multiple image options at different widths. The sizes attribute tells the browser which option to use based on layout.

<img
src="image-800.jpg"
srcset="
image-400.jpg 400w,
image-600.jpg 600w,
image-800.jpg 800w,
image-1000.jpg 1000w
"
sizes="(min-width: 1000px) 900px, calc(100vw - 2rem)"
alt="Product"
/>

The browser:

  1. Checks the current layout width.
  2. Evaluates the sizes attribute to determine the image's display size in the current layout.
  3. Checks the device's pixel density, such as 2x for Retina screens.
  4. Selects the smallest image from srcset that fits the required size.
  5. Downloads only that one image.

Shopify's image_tag Liquid filter generates responsive images with proper srcset, sizes, and dimensions:

{{ product.featured_image
| image_url: width: 1000
| image_tag:
widths: '400, 600, 800, 1000',
sizes: '(min-width: 1000px) 900px, calc(100vw - 2rem)'
}}

This generates a complete responsive image with:

  • A srcset at the specified widths.
  • Automatic height and width attributes, which prevent CLS.
  • Shopify CDN URLs with automatic format conversion.

Too many widths in srcset:

  • Hurts caching effectiveness.
  • The browser has to evaluate more options.
  • Four to six widths are usually sufficient.
  • You don't need a perfectly sized image for every pixel.

Wrong sizes attribute:

  • The browser downloads the wrong image.
  • It might download the largest image, wasting bandwidth.
  • It might download the smallest image, which looks pixelated.
  • Use the Responsive Image Linter extension to check.

No sizes attribute:

  • The browser defaults to downloading the largest image.
  • It wastes bandwidth on mobile.
  • LCP is slower.

The sizes attribute is notoriously difficult. Use the Responsive Image Linter Chrome extension:

  1. Install the extension.
  2. Put any value in the sizes attribute.
  3. The extension calculates the correct value.
  4. Update your code.

Keep the srcset suggestions reasonable. You don't need a perfectly sized image for every possible width. Fewer widths improve caching.

Anchor to Let the browser work out ,[object Object], for lazy-loaded imagesLet the browser work out sizes for lazy-loaded images

For images with loading="lazy", set sizes to auto instead of writing a breakpoint list. The browser uses the image's own layout width to pick a source, so the value can't drift out of sync with your CSS:

{{ product.featured_image
| image_url: width: 1000
| image_tag:
loading: 'lazy',
widths: '400, 600, 800, 1000',
sizes: 'auto'
}}

Shopify injects a polyfill for browsers that don't support sizes="auto" natively, so it's safe to use today.

Caution

auto applies only to images with loading="lazy". Eager images, including the LCP image, still need an explicit sizes value, because the browser has to pick a source before layout runs. Never lazy-load the LCP image to get auto: the lazy-loading delay costs far more than an imperfect sizes value.


Anchor to Special considerations for carousels, slideshows, and gridsSpecial considerations for carousels, slideshows, and grids

Components that contain many images require careful implementation to avoid hurting performance.

  • Load only what's needed: Don't render <img> tags for images that aren't visible. For a carousel, render <img> tags only for the first few images, and use JavaScript to add new <img> tags as the user navigates through the carousel. See Limit pagination depth for detailed carousel implementation patterns.
  • Prioritize visible images: Make sure the first, visible image in a slideshow or grid loads eagerly with loading="eager", and has fetchpriority="high" if it's the LCP element. All other non-visible images should use loading="lazy".

  • Responsive Image Linter: A Chrome extension that helps you validate and generate correct sizes attributes.
  • Performance panel, Insights tab: Run a measurement to see whether Image sizes are too large is flagged as an optimization opportunity.
  • Chrome DevTools Network panel: Verify that the correctly sized image downloads at different viewport sizes.
  • Device Mode: Test at different viewport sizes, such as mobile, tablet, and desktop, and compare the file sizes of images being loaded.


Was this page helpful?