Shopify image optimization. LCP + WebP.
How to cut Shopify image weight 60 percent in an afternoon: WebP conversion, responsive srcset, correct lazy-load attributes, and the image_url filter parameters Shopify's CDN accepts.
Four moves, 60 percent lighter.
Shopify image optimization in 2026 is four changes against the default theme. One, swap the legacy img_url Liquid filter for image_url with explicit width and format=webp; the CDN handles the conversion. Two, build a responsive srcset with discrete widths (400, 600, 800, 1200, 1600) and a sizes attribute that matches the theme's breakpoints. Three, tag the hero image with fetchpriority='high' rather than loading='lazy' so it downloads first; tag everything below the fold with loading='lazy'. Four, keep the hero under 200 KB on mobile (1200-pixel WebP at quality 80 usually lands there). These four moves cut LCP by 0.5 to 1.2 seconds on most Shopify stores and pull Core Web Vitals from failing to passing. Store-wide audit takes 2 to 3 hours; implementation takes an afternoon for the homepage and product template.
The filter upgrade does half the job.
Shopify's CDN has two Liquid filters for image URLs. The legacy img_url filter (pre-2022) returns the image at a fixed size and format; no WebP, no fine-grained width control. The modern image_url filter returns a full CDN URL with configurable width, height, crop, and format parameters. Switching from img_url to image_url is a 1-to-1 replacement in most themes and enables automatic WebP delivery via the CDN.
Concrete Liquid: {{ product.featured_image | image_url: width: 1200 }} returns a URL like /cdn/shop/files/image_1200x.jpg?v=xxx. Adding format: 'webp' forces WebP regardless of browser detection, which is what you want for browsers that accept WebP (all modern browsers in 2026). Adding width: 1200, height: 1200, crop: 'center' produces a square crop without the legacy _1200x1200.jpg filename convention.
The Shopify CDN caches every unique URL aggressively, so serving 5 different widths of the same image results in 5 cached variants. This is the right behavior; the browser picks the one it needs. See Shopify's image_url documentation for the full parameter reference. For most themes, the one-time find-and-replace from img_url to image_url across theme.liquid, snippets, and sections takes 1 to 2 hours and delivers 15 to 25 percent image-weight reduction with zero visible change.
Five widths, right-sized.
A responsive image on Shopify is an img tag with three attributes: src (fallback for browsers without srcset support, which in 2026 is essentially zero), srcset (comma-separated list of width variants), and sizes (CSS-like description of how wide the image renders at each breakpoint). The browser reads sizes, calculates the effective width for the current viewport, and picks the smallest srcset variant that meets or exceeds that width.
Typical pattern for a hero image: srcset widths at 400, 600, 800, 1200, 1600 pixels; sizes attribute like "(max-width: 640px) 100vw, (max-width: 1024px) 90vw, 1200px". The mobile user at 375px viewport gets the 400px variant (40 to 80 KB WebP). The desktop user at 1440px viewport gets the 1200px variant (150 to 250 KB WebP). The same image, one Liquid block, five orders of magnitude of byte savings between mobile and desktop on a heavy hero.
The mistake to avoid: building a srcset with 10 variants. The browser needs 4 to 5 steps to pick well; beyond that the CDN cache is fragmented across too many variants and the cache hit rate drops. Stick to 400, 600, 800, 1200, 1600 as standard; add 2000 for retina hero use cases. Product images on a collection grid typically need only 300, 400, 600, 800 because they never render at full width.
Hero eager, everything else lazy.
The loading="lazy" attribute tells the browser to defer image download until the image is close to entering the viewport. For images below the fold this saves initial page bytes and moves the work to when the user scrolls. For images above the fold it is harmful: the browser delays the download, LCP gets worse, and Core Web Vitals fail. The correct pattern is loading="eager" (or no loading attribute, which defaults to eager in 2026) for the hero, loading="lazy" for everything else.
The modern upgrade is fetchpriority="high" on the LCP image. This signals to the browser that the image is the most important resource on the page; it gets bandwidth priority ahead of scripts, fonts, and other images. On throttled mobile connections, fetchpriority="high" on the hero plus preloading the hero via <link rel="preload" as="image"> can cut LCP by another 200 to 400 milliseconds on top of the image-weight improvements.
The Liquid pattern for Shopify product pages: the first product image (index 0 in a forloop) gets eager + fetchpriority="high"; subsequent images get lazy. Google's web.dev LCP guide has the full decision tree; for Shopify specifically, the pattern fits cleanly into the existing section-rendering API without theme-file restructuring.
The CDN does more than you think.
Shopify's CDN (powered by Cloudflare and Fastly) handles WebP conversion, format negotiation via the Accept header, and automatic AVIF delivery to browsers that support it (currently iOS 16+ and Chrome 85+). AVIF is typically 20 to 30 percent smaller than equivalent WebP; on modern browsers that support it, the CDN serves AVIF automatically when you request format=webp, falling back to WebP for older browsers. The upshot: writing format=webp in your Liquid filter gets you AVIF for free on capable browsers.
The quality parameter matters. Default quality is 90 which is visually lossless but heavy. Quality 80 is indistinguishable for photography (hero banners, product images) and saves 25 to 35 percent. Quality 75 is fine for less-important images (thumbnails, blog headers) and saves another 10 to 15 percent. Quality 70 starts to show compression artifacts on solid colors and gradients; below that, visible banding appears in the sky on landscape hero images. Use image_url: width: 1200, quality: 80 as the default.
Dimensions and crop. Shopify's CDN accepts crop=center, crop=top, crop=bottom, crop=left, crop=right. For product listings where the product should always stay centered, crop=center with a square aspect (height: width equal) produces the cleanest grid. For lifestyle heroes where the subject sits off-center, leaving crop unspecified preserves the full image and letterboxes if needed. The crop decision is per-use-case; making it consistent within the theme reduces cognitive load for merchants uploading new images.
Three tools, two hours.
A store-wide image audit uses three tools in sequence. Shopify's admin speed report surfaces site-wide LCP and image findings flagged by Shopify's own crawler. PageSpeed Insights on the homepage, a product page, and a collection page shows Google's view with Core Web Vitals scores and specific opportunity warnings. Chrome DevTools Network tab, filtered to Img, on a throttled Slow 4G connection, shows actual bytes delivered per image on the page.
The pattern across most Shopify stores we audit: homepage LCP is acceptable because the theme optimizes the hero reasonably well by default, but product pages fail because merchants upload 3 to 5 MB raw images and the theme serves them at full resolution. Collection pages also fail when there are 24 products in a grid; that is 24 images loading eagerly when only the first 6 need to. Fixing the product template and the collection grid typically accounts for 70 percent of the storewide weight reduction.
For deeper optimization beyond image weight, see the broader Core Web Vitals work. Related posts (when Pillar 8 ships fully): server-side tracking setup (the JavaScript weight angle), GraphQL Admin API (for custom image metafield flows), and our Shopify speed optimization service.
Six answers.
Does Shopify automatically serve WebP images?
Shopify's CDN automatically serves WebP to browsers that accept it when you use the image_url filter with a format parameter or when the browser sends an Accept header including image/webp. The default Liquid image_url filter lets Shopify negotiate format based on the browser. Stores on older Liquid patterns using the legacy img_url filter do not get automatic WebP; upgrading to image_url is a 1-to-1 replacement and is the single highest-impact image change most Shopify stores can make.
What image size should the Shopify hero be in 2026?
Target under 200 KB for the above-the-fold hero image on mobile, under 350 KB on desktop. For 2026 retina displays, that typically means a source width of 1200 to 1600 pixels in WebP format with quality 75 to 85. Higher resolutions are diminishing returns because the browser downscales anyway. Run the image through Squoosh or Shopify's built-in CDN with &format=webp&quality=80 to get the weight down without visible quality loss. The LCP budget for a 2.5-second target on a mid-range mobile device is roughly 400 KB across all above-the-fold resources; the hero image should not consume more than half of that.
How do I implement responsive images correctly on Shopify?
Use the image_url filter with srcset and sizes attributes. Shopify's CDN accepts width parameters at discrete sizes (200, 400, 600, 800, 1000, 1200, 1600, 2000), so build a srcset covering the viewport sizes your theme supports. The sizes attribute tells the browser how wide the image will render at each breakpoint so it can pick the right source. A full example is in the body of this post. The common mistake is serving the same 2000-pixel image to both desktop and mobile; on mobile that is 4x the bytes the phone actually needs.
Should I use loading='lazy' on every image?
Every image except the hero (LCP) and above-the-fold product images. Lazy loading the hero delays LCP by 200 to 800 milliseconds because the browser does not prioritize the download. For the hero, use fetchpriority='high' instead; the browser fetches it before other resources. For everything below the fold, loading='lazy' saves initial page bytes and moves the image download to when the user scrolls near it. Shopify's section-rendering API makes this easy: the first image in a collection or product loop gets eager loading, the rest get lazy.
What LCP score should a Shopify store target?
Target under 2.5 seconds on mobile to pass Core Web Vitals; under 1.8 seconds to be best-in-class. Shopify stores out of the box often land at 3 to 4 seconds on mobile because hero images are oversized and themes load non-critical JavaScript early. Fixing image weight typically cuts LCP by 0.5 to 1.2 seconds; combined with font preloading and JavaScript deferral it is possible to move most Shopify stores from 3.5 to under 2.0 in a 2-week engagement. Test via PageSpeed Insights with mobile simulated Moto G4 on 4G; that is Google's measurement baseline.
How do I audit Shopify image weight across the store?
Three tools that together cover the audit. One, Shopify's built-in speed report in the admin shows site-wide LCP and image-specific findings. Two, PageSpeed Insights on the homepage, a product page, and a collection page gives Google's view. Three, Chrome DevTools Network tab filtered to Img on a throttled connection shows actual bytes delivered. The common pattern: homepage passes, product pages fail because the main product image is 3 to 5 MB raw uploads rather than optimized. A store-wide audit usually takes 2 to 3 hours for a mid-size catalog and surfaces 80 percent of the image issues.
Speed is conversion.
Our Shopify speed-optimization engagements ship a 2-week Core Web Vitals overhaul: image audit, LCP tuning, CLS fixes, and a 30-day retention report. Scoped quote in 48 hours.