Quick Fix with Thunder
Image sizing is partly delivery and partly markup. Thunder helps with the delivery side โ script defer, asset hints, and loading order โ but the actual widths your theme requests are decided in Liquid. If your theme uses a clean image_tag pattern, Thunder makes the rest of the page get out of the way so the right image lands faster.
Install Thunder to remove competing app and theme scripts from the LCP path, then use the manual steps below to fix the Liquid that requests the wrong widths. For supporting fixes, see the Shopify image optimization guide and the WebP and AVIF guide.
Install ThunderWhat the "Properly Size Images" Warning Actually Checks
Lighthouse compares the natural dimensions of each downloaded image with the dimensions the browser actually rendered. If a 2400ร1600 image lands on the page only to be displayed at 600ร400, the audit flags it. The downloaded file is several times larger than necessary, the decode is slower, and on a mid-range phone the LCP element appears later than it should.
The fix is what Google calls responsive images: generate multiple versions, then tell the browser which one to use based on the viewport and slot. On Shopify, you do that with image_url, image_tag, and the widths and sizes attributes. Get those right and the warning disappears for the section.
This is a different problem from "Serve images in next-gen formats" (covered in the WebP and AVIF guide) and from "Defer offscreen images" (covered in the lazy loading guide). Properly size images is purely about dimensions.
Why Shopify Themes Trigger This Warning
Single hardcoded width
Liquid passes width: 2000 or higher everywhere, so the CDN always returns one oversized file regardless of viewport.
Missing or wrong sizes
A srcset is generated but the sizes attribute is missing, so the browser assumes 100vw and pulls the biggest candidate on every device.
Page builder defaults
Page builders, lookbook sections, and lifestyle blocks often inject raw img tags with the original asset URL โ no srcset at all.
Step 1: Audit Which Images Are Actually Oversized
Run PageSpeed Insights on a real product page, a homepage, and a collection page. Open the "Properly size images" diagnostic and read each row. Lighthouse tells you the URL, the rendered size, the intrinsic size, and the potential savings. That list is your work order.
For a second opinion, open Chrome DevTools, switch to a mobile profile, and load the page. Right-click each above-the-fold image and pick "Inspect". Compare the natural width with the rendered width. A 2400px image inside a 400px slot is the textbook offender.
If you also see warnings for hero LCP and network payload, read the hero image preload guide and the enormous network payloads guide in parallel โ they often share a root cause.
Step 2: Fix Hero Images with image_tag, widths, and sizes
A full-bleed hero is the most visible offender. Replace any hardcoded hero with an image_tag filter that produces a real srcset and a sizes attribute matched to the layout. image_tag also writes width and height attributes from the source, which prevents the layout shift the warning often hides.
{{ section.settings.image
| image_url: width: 2000
| image_tag:
widths: '600, 900, 1200, 1600, 2000',
sizes: '100vw',
loading: 'eager',
fetchpriority: 'high',
alt: section.settings.heading
}}
The widths list tells image_tag which CDN sizes to generate. The sizes attribute tells the browser how wide the image will render at each breakpoint. For a hero that fills the viewport, 100vw is correct. For a hero that caps at a max-width, write something like (min-width: 1280px) 1280px, 100vw.
Step 3: Fix Product Grids โ They Almost Never Need 1600px
A collection card on mobile renders at maybe 180-220px wide. On desktop, a four-column grid lands at around 280-360px per card. Requesting a 2000px image for that slot is pure waste. Cap product grid widths aggressively and let srcset pick the smallest candidate that satisfies the layout.
{{ product.featured_image
| image_url: width: 800
| image_tag:
widths: '200, 300, 400, 600, 800',
sizes: '(min-width: 990px) 25vw, (min-width: 750px) 33vw, 50vw',
loading: 'lazy',
alt: product.title
}} The sizes attribute here describes a four-column grid above 990px, three columns above 750px, and two columns on mobile. The browser does the math and picks the right candidate โ usually 200-400px on mobile and 400-600px on desktop. For more on collection performance, see the collection page speed guide.
Step 4: Stop Mobile Downloading 4000px Originals
Shopify's image_url filter accepts widths up to 5760px and auto-calculates height when only width is set. That capability is for retina desktop displays, not for a 360px-wide phone. If a section is hardcoded with width: 4000, mobile visitors will download that file even though it gets resized to a fraction of that on screen.
Grep your theme for large widths and replace them. The rule of thumb: the largest width you request should be roughly twice the largest slot the image will ever fill. A 1200px-wide hero on a desktop layout can request up to 2400px to look sharp on retina. A 360px product thumbnail does not need anything above 600-800px.
{%- comment -%}Before: oversized request for every viewport{%- endcomment -%}
{{ image | image_url: width: 4000 | image_tag: alt: image.alt }}
{%- comment -%}After: responsive request capped at sane widths{%- endcomment -%}
{{ image
| image_url: width: 1600
| image_tag:
widths: '400, 600, 800, 1200, 1600',
sizes: '(min-width: 990px) 50vw, 100vw',
loading: 'lazy',
alt: image.alt
}}
For art-directed sections where mobile gets a different crop than desktop, use a <picture> element with <source media> tags. That is the only way to swap the file based on viewport, not just the file size.
Step 5: Match sizes to the Actual Layout
The sizes attribute is where most "Properly size images" fixes still go wrong. A srcset without sizes defaults to 100vw, so the browser pulls the biggest candidate every time. The sizes string should describe how wide the rendered image is at each breakpoint โ not the natural size of the file.
- Full-bleed hero:
sizes="100vw" - Two-up product page gallery:
sizes="(min-width: 990px) 50vw, 100vw" - Three-column lookbook:
sizes="(min-width: 990px) 33vw, 100vw" - Four-column collection grid:
sizes="(min-width: 990px) 25vw, (min-width: 750px) 33vw, 50vw" - Fixed sidebar thumbnail:
sizes="120px"
If you change a section from three columns to four, update the sizes string with it. PageSpeed Insights will silently re-flag the page if the math no longer matches the CSS.
Step 6: Watch Out for Page Builder and App Image Tags
Page builders, lookbook apps, story sections, and quick-view modals frequently bypass Shopify's image_tag helpers and inject raw img tags pointing at the original asset URL. Those tags have no srcset and no sizes, and the browser has no choice but to download the full-resolution file.
Open the rendered HTML for any flagged image. If you do not see srcset and sizes attributes, find the section or app responsible and either rewrite it to use Shopify helpers or remove the section if it is not essential. If a third-party section will not cooperate, the third-party scripts guide covers your deferral options.
For automated app and script optimization across the rest of the page, see Thunder's features.
Manual Fix vs Thunder Fix
| Image issue | Manual fix | Thunder fix |
|---|---|---|
| Hardcoded oversized widths | Replace with image_tag plus a real widths list | Theme Liquid still controls the width โ manual edit required |
| Missing srcset on app sections | Rewrite section or remove if non-essential | Defers competing app scripts so the right image lands sooner |
| Wrong sizes attribute | Match sizes string to the CSS column count and breakpoints | Does not change Liquid โ pair with manual sizes update |
| Hero file too large on mobile | Cap widths, set fetchpriority high, preload only the hero | Removes script competition so the hero downloads earlier |
Retest Without Chasing One-Off Noise
Lighthouse mobile tests vary. Run PageSpeed Insights two or three times on the same page and compare the median. The "Properly size images" diagnostic itself is pretty stable โ if Lighthouse keeps flagging the same files, the markup is wrong. If it flags different files each run, you might be hitting variant images that are out of policy and need a one-off cleanup.
After image fixes, look at related warnings. If TBT stays high, see the Shopify Total Blocking Time guide. If long dependency chains remain, see the critical request chains guide. If caching is flagged, see the efficient cache policy guide. For broader work, the complete Shopify speed optimization guide is the pillar reference, and Thunder pricing covers managed plans.
References and Validation
Chrome's "Properly size images" documentation spells out that the fix is responsive images: generate multiple versions and specify the correct one with srcset and sizes. Shopify's image_url documentation confirms widths up to 5760px and auto-height when only width is specified, and the image_tag documentation shows how it generates width and height attributes for you.
The fix is unglamorous but reliable: cap widths, pass a real widths list, write a sizes attribute that matches the layout, and let image_tag handle width and height. Skip these and the warning will keep coming back every time PageSpeed reruns.
Done For You
Core Web Vitals guarantee ยท 2-week delivery ยท 6 months Thunder free
Get Expert Optimization โStarting from โฌ1,500
FAQ
What does "Properly size images" mean on Shopify?
Lighthouse flags images that are downloaded larger than they are displayed. On Shopify, this usually means the theme requested a 2000-4000px source from the CDN for a slot that renders at 300-800px on mobile. The browser still has to download, decode, and resize the oversized file.
Doesn't Shopify automatically resize images?
Shopify's CDN can deliver many sizes, but your Liquid decides which one to request. If your theme passes width: 2000 to image_url everywhere, that is what the CDN will serve, even if the slot is small. You have to request the right widths and let srcset pick the smallest match.
What is the right width to request for a Shopify hero image?
For a full-width hero, generate a srcset that covers common viewport widths โ typically 600, 900, 1200, 1600, and 2000px โ and let the browser pick. Avoid requesting widths above 2000-2400px unless you are targeting retina displays at desktop sizes. image_url supports widths up to 5760px but you almost never need that.
Can Thunder properly size images automatically?
Thunder optimizes script and asset loading and can help with image delivery in supported themes, but Shopify themes ultimately control the widths and sizes attributes in Liquid. For custom hero or product templates, you may still need to update image_url and image_tag calls so the CDN serves the right size.
Should I use image_tag or write img tags manually?
image_tag is usually safer because it generates width and height attributes from the source image, which helps Cumulative Layout Shift. You can pass widths, sizes, loading, and fetchpriority directly. Manual img tags work too, but you have to set width, height, srcset, and sizes yourself.
Does fixing properly size images also help LCP?
Often yes. If the LCP element is a hero image, serving a smaller file means faster download and faster paint. Combine correct widths with fetchpriority high and a preload for the hero, and you usually see LCP improve as well as the warning disappearing.