
Increasing site speed by 20% using Shopify's image compression (free code snippet)
The Complete Code (Copy & Paste Ready)
Save this as img-compressor.liquid in your snippets folder:
{% comment %}
Properties:
- image: Image
- max_width: number
- alt: string
- class: string
- preload: boolean (optional)
- lazy: boolean (optional)
{% endcomment %}
{% assign widths = "100,200,300,400,600,800,1000" | split: "," %}
{% assign srcset_values = " " %}
{% assign max_width = max_width | plus: 0 | default: 1000 %}
{% for width in widths %}
{% assign width_num = width | plus: 0 %}
{% if width_num <= max_width %}
{% assign srcset_item = image | image_url: width: width_num | append: " " | append: width | append: "w" %}
{% if srcset_values != "" %}
{% assign srcset_values = srcset_values | append: ", " %}
{% endif %}
{% assign srcset_values = srcset_values | append: srcset_item %}
{% endif %}
{% endfor %}
<img src="{{ image | image_url: width: max_width }}"
srcset="{{ srcset_values }}"
sizes="
(max-width: 480px) 400px,
(max-width: 768px) 600px,
(max-width: 992px) 600px,
(max-width: 1200px) 800px,
{{ max_width }}px"
width="{{ max_width }}" height="{{ max_width }}"
alt="{{ alt }}"
class="{{ class }}"
{% if preload == true %}
fetchpriority="high"
{% endif %}
{% if lazy == true %}
loading="lazy"
{% endif %}
/>
Why Your Images Are Killing Your Site Speed
If you've been wondering why your Shopify store loads slower than molasses, there's a good chance your images are the culprit. I've seen stores with gorgeous 4K product photos that take 8 seconds to load on mobile. Your customers aren't waiting around for that.
The snippet above fixes this mess by automatically creating multiple sizes of each image and letting the browser pick the right one. Mobile users get small, fast images. Desktop users get crisp, detailed ones. Everyone wins.
What This Code Actually Does
Here's the breakdown without the fluff:
It Creates Multiple Image Sizes
The code takes your original image and creates versions at 100px, 200px, 300px, 400px, 600px, 800px, and 1000px wide. But here's the smart part - it only creates sizes up to your max_width. So if you set max_width: 600, it won't waste time generating the 800px and 1000px versions.
It Tells Browsers Which Size to Use
The sizes attribute is like a roadmap for browsers:
- Phone screens (under 480px): Use the 400px image
- Tablets (under 768px): Use the 600px image
- Laptops (under 992px): Use the 600px image
- Desktops (under 1200px): Use the 800px image
- Big screens: Use your max_width
It Handles Loading Priorities
The preload and lazy options let you fine-tune which images load first. Use preload: true for your hero image that appears immediately. Use lazy: true for everything else so they only load when needed.
How to Use It
Basic Usage
{% render 'img-compressor',
image: product.featured_image,
max_width: 800,
alt: product.title,
class: 'product-image' %}
Load Your Hero Image First
{% render 'img-compressor',
image: section.settings.hero_image,
max_width: 1200,
alt: 'Hero banner',
class: 'hero-image',
preload: true %}
Lazy Load Product Grids
{% for product in collection.products %}
{% render 'img-compressor',
image: product.featured_image,
max_width: 400,
alt: product.title,
class: 'product-grid-image',
lazy: true %}
{% endfor %}
The Max Width Trap (Don't Fall Into It)
Getting max_width wrong is the fastest way to screw this up. I've seen developers set it to 2000px "just to be safe" and wonder why their site is still slow.
Here's what actually works:
Product grid images: 400-500px max. These are small thumbnails. A 1000px image is massive overkill.
Main product images: 800-1000px. This covers most product detail pages without going overboard.
Hero banners: 1200-1600px. These span the full width on large screens, so they need the resolution.
Blog images: 600-800px. Perfect for article content without bloating file sizes.
The rule of thumb: Look at how big your image actually displays on screen, then add maybe 20% for retina displays. That's your max_width.
When to Preload vs Lazy Load
Preload these:
- Your hero image (the big one at the top)
- Featured product image on product pages
- Logo or brand images that appear immediately
Lazy load these:
- Product grid images
- Blog post images
- Anything below the fold
- Gallery images
Never use both preload and lazy on the same image. They cancel each other out.
Common Mistakes I See
Setting Max Width Too High
You're displaying a 300px product thumbnail but setting max_width: 1500. Now you're serving a 200KB image instead of a 30KB one. Your customers on mobile data plans hate this.
Lazy Loading Above-the-Fold Images
Your hero image has lazy: true and now it loads after everything else. Your Largest Contentful Paint score tanks and Google penalizes your rankings.
Wrong Sizes Attribute
Your CSS breakpoints are at 576px, 768px, and 992px, but the snippet uses 480px, 768px, and 992px. The browser is picking the wrong image sizes.
Fix it by customizing the sizes:
sizes="
(max-width: 575px) 300px,
(max-width: 767px) 400px,
(max-width: 991px) 500px,
800px"
Missing Alt Text
Screen readers need alt text. Search engines need alt text. You need alt text. Don't leave it empty.
{% assign alt_text = product.title | append: ' - ' | append: product.vendor %}
{% render 'img-compressor',
image: product.featured_image,
alt: alt_text %}
Making It Work for Your Theme
Match Your Breakpoints
If your theme uses different responsive breakpoints, update the sizes attribute to match. Check your CSS for media queries and use those same pixel values.
Context-Aware Sizing
{% case template %}
{% when 'product' %}
{% assign max_width = 1000 %}
{% when 'collection' %}
{% assign max_width = 400 %}
{% else %}
{% assign max_width = 600 %}
{% endcase %}
{% render 'img-compressor',
image: image,
max_width: max_width %}
Conditional Loading
{% assign is_above_fold = forloop.index <= 4 %}
{% render 'img-compressor',
image: product.featured_image,
preload: is_above_fold,
lazy: is_above_fold == false %}
How to Know It's Working
Speed Tests
Run your site through Google PageSpeed Insights before and after. Look for improvements in:
- Largest Contentful Paint (LCP)
- Cumulative Layout Shift (CLS)
- Overall performance score
File Size Checks
Open Chrome DevTools, go to Network tab, and reload your page. Check the image file sizes. You should see different sizes being loaded for different screen widths.
Mobile Performance
Test on actual mobile devices or use Chrome's device emulation. Mobile improvements are usually the most dramatic.
The Bottom Line
This snippet won't magically fix a slow site, but it'll make a solid dent in your image loading times. The key is using it thoughtfully - set appropriate max widths, preload critical images, and lazy load everything else.
Your customers will notice faster loading times. Google will notice better Core Web Vitals. Your conversion rates will notice the difference too.