PERFORMANCE · CORE WEB VITALS · THEME · 11 MARCH 2025 · 7 MIN READ
Image delivery: formats, sizing and the CDN you already have
Shopify already resizes, converts and caches your images. Most slow stores are slow because the theme asks for the wrong size, not because the CDN is missing.
Ask the Shopify CDN for the size you are actually going to display, using the image_url filter with a width, and let it handle format conversion and caching. Almost every image problem on a Shopify store is a theme requesting a 2,048px file for a 400px slot, or lazy-loading the hero image that Largest Contentful Paint is waiting for. You do not need another image app, another CDN, or a build step — you need srcset with sensible widths, correct width and height attributes to stop the page shifting, and loading="eager" on exactly the one image that appears above the fold.
IN SHORT
- The Shopify CDN resizes on request and caches the result: `{{ product | image_url: width: 450 }}` gives you a 450px file, not a resized 4,000px one.
- The most common real problem is a theme serving one large file to every breakpoint. Fix it with `srcset` and a truthful `sizes`.
- Never lazy-load your LCP image. That single attribute costs more than every other image tweak combined.
- Always set `width` and `height` so the browser reserves the space — missing dimensions are the main cause of layout shift.
- Upload once at a sensible maximum. `image_url` caps at 5,760px, and nothing on a storefront needs that.
What Shopify is already doing for you
Every image you upload is served from Shopify’s CDN, and the CDN will resize it on request. The image_url filter is how you ask: {{ product | image_url: width: 450 }} returns a URL for a 450-pixel-wide version, generated once and then cached. width and height are the parameters that matter — give one and the other is calculated — and quality accepts 10 to 90 if you need to trade sharpness for bytes on a specific slot.
This is the part people reach past. Stores install an image optimisation app to compress files that the CDN was going to convert and cache anyway, and end up with two systems doing one job — one of which now sits in the critical path and injects script.
Before you add anything, check what the theme is requesting. In most slow stores the answer is one enormous file everywhere, and the CDN faithfully delivering it.
The three faults, in order of cost
These turn up repeatedly, and they are worth more than any amount of compression tuning.
- The hero image is lazy-loaded. The browser defers the one image the page is judged on, and Largest Contentful Paint waits for it. Above-the-fold images load eagerly; everything below the fold gets
loading="lazy". - One size for every screen. A phone downloads the desktop image.
srcsetwith a handful of widths and asizesattribute that tells the truth about the slot fixes it. - No
widthandheightattributes. The browser cannot reserve space, so the page jumps as each image arrives — which is the bulk of most stores’ layout shift score.
srcset, and the sizes attribute people get wrong
A srcset lists the candidate files. The sizes attribute tells the browser how wide the image will actually be at a given breakpoint, and the browser uses it to pick — before it has laid the page out, which is why it cannot work this out for itself.
The usual mistake is writing sizes="100vw" on an image that occupies half a two-column row. The browser then downloads a file twice as large as it needs, on every screen, forever. Getting sizes right on the four or five image slots that appear on every page is a twenty-minute job with a measurable result.
Keep the candidate list short. Widths at roughly 400, 600, 900, 1,200 and 1,800 cover real devices; generating fifteen variants per image just fills the cache with files nobody requests.
Formats: mostly not your decision
Shopify’s CDN handles format negotiation, serving a modern format to browsers that accept it. That is a good default and it removes the need for the conversion pipeline a headless build would have to own.
What is still your decision is what you upload. Upload a source large enough for the biggest slot that will ever use it and no larger — a 6,000px product shot to fill a 1,200px frame costs you storage and nothing else. Keep photographs as JPEG sources; keep flat graphics, logos and line art as SVG, where they stay sharp at any size and usually weigh less than any raster version.
The one place to intervene manually is a hero photograph that stays soft at the quality default. Drop quality to 70 on that slot, compare them side by side at full size, and move on. It is not a site-wide setting.
How to tell whether images are actually your problem
Run the page through a lab test and look at what the LCP element is and how long it takes. If LCP is an image and it is arriving late, the ordering above will fix most of it. If LCP is a block of text that is waiting on a web font, or the page spends two seconds executing third-party script before it paints, no amount of image work will help — you have an app problem or a font problem wearing an image problem’s clothes.
That distinction is worth making before any work starts. We have seen stores spend a sprint on image delivery to gain a tenth of a second, while eleven script-injecting apps sat above them in the waterfall.
Questions this raises
How should images be optimised on Shopify?
Request the size you will display using the `image_url` filter with a `width`, provide a `srcset` with a handful of candidate widths and a truthful `sizes` attribute, set `width` and `height` on every image so the browser reserves space, and load the above-the-fold image eagerly while lazy-loading the rest. The CDN handles resizing, format conversion and caching.
Do you need an image optimisation app on Shopify?
Usually not. The CDN already resizes on request, converts format and caches the result. An app that compresses files the CDN was going to handle adds a second system, and often script, to do a job that is already done.
Why is my Largest Contentful Paint slow when images are small?
Most often because the LCP image is lazy-loaded, so the browser deliberately defers it. Check whether the element LCP is measuring is even an image — if it is text waiting on a web font, or the page is blocked executing third-party scripts, image work will not move the number.
What size should images be uploaded at?
Large enough for the biggest slot that will ever display them, and no larger. `image_url` accepts up to 5,760px, which is far beyond anything a storefront layout needs. Photographs as JPEG, flat graphics and logos as SVG.
What causes layout shift from images?
Missing `width` and `height` attributes. Without them the browser cannot reserve the space before the file arrives, so everything below jumps when it does. Setting both on every image removes most of a typical store’s Cumulative Layout Shift.
NEXT STEP
Free store audit
A senior Shopify engineer reviews your storefront, theme performance and checkout, then sends a prioritised list of fixes.
