Shopify Images: Fixing Image Optimisation in Shopify
Shopify already does a lot for your images. Every file goes through its CDN, which can resize on request and send modern formats to browsers that accept them. So when the Image Optimisation audit flags a Shopify store, the cause is rarely the CDN.
It is usually the theme asking for the wrong width, an old tag that never asks for a width at all, the first product photo lazy-loaded, alt text left blank, or images pasted into descriptions and theme files where the CDN cannot resize them. This guide works through each of those in the theme code and the admin.
- What Shopify does for you, and what it leaves to the theme
- Reading Shopify image addresses
- Image code for modern Shopify themes
- Getting widths and sizes right
- The fix, step by step
- Variant images and galleries
- Images that bypass the resizing pipeline
- Keeping a Shopify store in good shape
- Questions and answers
What Shopify does for you, and what it leaves to the theme
Images you upload to products, collections, the Files area or theme image settings are stored by Shopify
and served from its CDN at cdn.shopify.com, or through your own domain on newer setups.
The CDN produces resized copies on request, so one upload can be served at many widths, and it chooses
a modern format such as WebP for browsers that accept it. You do not need an extra CDN or format plugin
for that part.
What the CDN cannot decide is how the theme asks for images. That is controlled by Liquid in your theme and by what you enter in the admin:
- Which width is requested, and whether a
srcsetoffers smaller ones for phones. - Whether the
sizesattribute tells the browser how wide the image really is on screen. - Whether width and height are printed, so space is reserved.
- Whether the first large image is loaded early or lazy-loaded.
- Whether alt text exists and is printed.
- Whether an image comes through the resizing pipeline at all, or is a fixed file in the theme or a product description.
Because the CDN makes new copies at the requested width, the weight of your original upload matters less on Shopify than on a self-hosted site. A 6 MB original requested at 800 pixels wide is delivered as a much smaller 800 pixel file. The biggest wins therefore come from the theme and the admin, not from re-compressing originals.
Reading Shopify image addresses
View the source of a product page and look at the image addresses. They tell you which Liquid produced them and whether the CDN is being asked to resize.
| What you see in the URL | What it means |
|---|---|
...oak-table.jpg?v=1712345678&width=800 | Current image_url filter with a width. Good. |
...oak-table_800x.jpg?v=1712345678 | Size suffix from the older img_url
filter. It works, but the theme is using deprecated code and often has no srcset. |
...oak-table.jpg?v=1712345678 with no width | The original may be served at full size. Check what the tag requested. |
...oak-table_master.jpg or _original | Very old theme code asking for the full original. |
/cdn/shop/t/12/assets/banner.jpg | A file in the theme's assets folder, delivered as uploaded, not resized. |
In developer tools, the Network panel shows the format actually delivered in its Type column, which
may be WebP even when the URL ends in .jpg, because the CDN negotiates the format.
Image code for modern Shopify themes
The current filters are image_url, which builds a CDN address for a given width or height,
and image_tag, which turns that address into a complete <img> element.
image_tag prints width and height, builds a srcset
of smaller widths up to the one you requested, uses the image's stored alt text by default, and passes
through extra attributes such as loading, fetchpriority and class.
The older img_url and img_tag filters are deprecated.
A product gallery
{%- for media in product.media -%}
{%- if media.media_type == 'image' -%}
{%- liquid
if forloop.first
assign img_loading = 'eager'
assign img_priority = 'high'
else
assign img_loading = 'lazy'
assign img_priority = 'auto'
endif
-%}
{{ media.preview_image
| image_url: width: 1600
| image_tag:
widths: '400, 600, 800, 1200, 1600',
sizes: '(min-width: 990px) 55vw, 100vw',
loading: img_loading,
fetchpriority: img_priority,
alt: media.alt }}
{%- endif -%}
{%- endfor -%}
Only the first media item is loaded eagerly with high priority, because it is usually the Largest Contentful Paint element on a product page. The rest wait until the visitor scrolls or swipes near them.
Replacing an old tag
{%- comment -%} Before: deprecated, fixed size, no srcset or dimensions {%- endcomment -%}
<img src="{{ product.featured_image | img_url: '1024x' }}" alt="{{ product.title }}">
{%- comment -%} After {%- endcomment -%}
{{ product.featured_image
| image_url: width: 1200
| image_tag: widths: '360, 540, 720, 900, 1200',
sizes: '(min-width: 750px) 50vw, 100vw' }}
Notice the old tag also printed the product title as alt text on every image. The new version uses the alt text stored on the image itself, which you can make distinct for each photo.
Getting widths and sizes right
The largest width you request should cover the biggest the image is ever shown, allowing for high-density screens. A product image shown at about 700 CSS pixels on a desktop needs roughly 1400 pixels for a sharp result on a high-density screen; requesting 3000 wastes bytes on every desktop visit.
The sizes attribute is where Shopify themes most often go wrong. It tells the browser
how wide the image will be displayed at each screen width, before the layout is known, so it can choose
the right file from the srcset. If a collection grid shows four products per row on desktop but sizes
says 100vw, the browser assumes each card image is full width and downloads a file roughly
four times wider than needed.
- Full-width banner:
100vwis correct. - Product gallery in a two-column layout: something like
(min-width: 990px) 55vw, 100vw. - Collection grid, four across on desktop and two on mobile: something like
(min-width: 990px) 25vw, 50vw.
Measure the real width of the image in developer tools at a few screen sizes and write sizes
to match. It does not need to be exact, but it should not be several times too large.
The fix, step by step
Step 1
Audit a product, collection and home page
Run the Image Optimisation audit on one typical product page, one collection page and the home page, because each uses different theme sections. Open the Network panel in developer tools on each, filter by images and sort by size. Note the heaviest images, the widths in their URLs, whether any use the old size suffix, and whether the first product photo is lazy-loaded or missing alt text.
Step 2
Find the Liquid that outputs each image
Duplicate your live theme and open the copy in the code editor or with Shopify CLI. Search
the sections and snippets folders for img_url, img_tag, image_url
and asset_url to find every place images are printed. Running Theme Check from Shopify CLI
also flags deprecated filters and image tags without width and height, which gives you a checklist of
files to change.
Step 3
Replace old img_url tags with image_url and image_tag
Rewrite each hand-built image tag that uses img_url so it uses image_url
with a width, piped into image_tag. That adds width and height, a srcset of smaller files
and the stored alt text in one go. Keep any classes the theme's CSS or scripts rely on by passing them
as a class parameter, and check the page layout after each change in the theme preview.
Step 4
Ask for sensible widths and an honest sizes value
For each image, set the requested width to about twice the largest size it
is displayed at, and list smaller widths for phones. Then measure how wide the image really appears at
mobile, tablet and desktop widths and write a sizes value to match. Collection grids and
product galleries are the usual offenders, where a default of 100vw makes browsers download files far
wider than the card.
Step 5
Load the first product image early and the rest lazily
On product templates, set the first media item to load eagerly with fetchpriority
set to high, and lazy-load every later gallery image and thumbnail. Do the same for the first banner
on the home page. Check the theme settings as well as the code, because some themes and apps add lazy-loading
to every image, including the one that decides how quickly the page appears.
Step 6
Fill in alt text in the admin or by CSV
Open a product in the admin, select each media item and add alt text describing that photo: the product and what differs, such as the angle, colour or detail. For large catalogues, export products to CSV, fill in the Image Alt Text column in a spreadsheet and import it again. Then confirm the theme prints the stored alt rather than repeating the product title on every image.
Step 7
Move fixed-size images into resizable fields
Look for images the CDN cannot resize: files in the theme assets folder called with asset_url,
large photos pasted into product descriptions and pages, and images added by apps. Move banners and content
photos into section image settings, metafields or the Files area so Liquid can request the right width.
Preview, publish the duplicate theme, and re-run the audit on the same three pages.
Variant images and galleries
Stores with colours or finishes often have the most image problems, because each variant adds photos and each photo needs its own alt text.
- Link each variant to its image in the product admin, so choosing a colour shows
the right photo. In Liquid, the selected variant's image is available as
product.selected_or_first_available_variant.featured_media, which lets the page load with the correct photo when a variant is chosen by URL. - Check the variant switch script. When a visitor chooses a variant, theme JavaScript
swaps the main image. Make sure it swaps the
srcsetas well as thesrc, or phones may download a full-size image on every change. - Describe each variant photo. Alt text such as "Oak bedside table in dark walnut finish" helps both screen reader users and image search more than the product title repeated on every colour.
- Keep ratios consistent. Photograph variants at the same shape so the gallery does not change height when the image swaps. Layout shift on Shopify is covered in detail in the Shopify layout shift guide.
Images that bypass the resizing pipeline
Images in product descriptions and pages
Images inserted through the rich text editor are placed in the HTML with a fixed address and size,
so they get no srcset and every visitor receives the same file. Keep description images modest in size
before inserting them, set their alt text in the editor's image dialog, and for image-heavy content consider
using theme sections or metafields instead, which go through image_url.
Images in the theme's assets folder
Files referenced with asset_url are delivered as uploaded. That is fine for small icons
and SVG logos, but a large banner photo stored there is served at full size to every device. Move it
to an image setting in the section, so the merchant can change it in the theme editor and Liquid can
resize it.
Images added by apps
Review apps, upsell widgets and page builders sometimes insert their own images with no width, height or lazy-loading. Test pages with each app's block temporarily disabled in the theme editor to see which app adds the weight, then check the app's settings or contact its developer.
Keeping a Shopify store in good shape
- Run Theme Check before publishing theme changes, so deprecated filters and unsized images are caught early.
- Recheck after theme updates. Updating to a new theme version can overwrite your Liquid changes; keep a note of every file you edited.
- Add alt text as part of product setup, in the admin or the import CSV, so new products never go live without it.
- Keep content images out of descriptions where a section or metafield can do the job.
- Trial apps on a duplicate theme and run the audit before and after installing them.
For the wider picture of how images affect speed on Shopify, see the Shopify page speed guide, and for writing product photo alt text, the product images guide.
1. Does Shopify optimise images automatically?
Partly. Shopify stores your uploads and serves them through its CDN, which creates resized copies at whatever width the theme requests and sends a modern format such as WebP to browsers that accept it. What it cannot do is decide which width your theme asks for, write an accurate sizes attribute, choose which images load first, or write alt text. Those depend on your theme code and on what you enter in the admin, which is where most audit findings come from.
2. Do I need an image compression app for Shopify?
Often not for product and collection images, because the CDN makes new, smaller copies at the width your theme requests, so the weight of the original matters less than on a self-hosted site. Apps can help with images that bypass resizing, such as large files in the theme assets folder or pasted into descriptions, and with bulk alt text. Before running one, check whether it replaces your originals, and keep a backup or export first.
3. What is the difference between img_url and image_url in Shopify?
img_url is the older Liquid filter, which used size strings such as 800x and produced addresses with a size suffix in the file name. It is deprecated. image_url is its replacement: you pass a width or height as named parameters, and it pairs with the image_tag filter, which prints a complete img element with width, height, a srcset and the stored alt text. Themes using img_url usually lack srcset and dimensions.
4. How do I add alt text to Shopify product images?
Open the product in the admin, select an image in the media section and use the option to add alt text, then save. Write a short description of that particular photo, such as the product plus the angle, colour or detail shown. For many products, export them to CSV, fill in the Image Alt Text column and import the file again. Afterwards check your theme prints the stored alt rather than the product title.
5. Why is my first Shopify product image loading slowly?
Common causes are the theme lazy-loading every image including the first, a very large requested width, a sizes value that makes the browser choose too big a file, or an app or slider script that inserts the image late. Load the first product image eagerly with fetchpriority set to high, request a width about twice its displayed size, and write sizes to match the layout. Then retest the product page in developer tools.
6. Why do images in my product descriptions not resize?
Images inserted through the rich text editor are placed in the description HTML with a fixed address, so the theme cannot add a srcset or ask the CDN for smaller versions. Every visitor, including phone users, downloads the same file. Resize those images before inserting them, add alt text in the image dialog, and for image-heavy content consider theme sections or metafields, which pass images through image_url and image_tag so they can be resized.
7. Will updating my Shopify theme undo my image fixes?
It can. Updating a theme to a new version usually means installing the new version and moving your settings across, and any Liquid edits you made to sections and snippets may not carry over. Keep a list of every file you changed, and after an update compare those files and re-apply your changes where needed. Run Theme Check and the Image Optimisation audit on the updated theme before publishing it to catch anything missed.
The Image Optimisation audit checks every image on your store page for file size, format, dimensions, alt text, lazy-loading, layout shift and CDN delivery, and ranks its recommendations by their effect on loading and bandwidth.
Open Image Optimisation Fixes โ