A slow Shopify store costs you customers. Studies show that even a one-second delay can drop conversions by 7%. The good news? Many speed improvements come down to small code changes in your theme.
This guide shares practical Liquid code snippets based on Shopify’s own performance recommendations. Even if you’re not a developer, understanding these will help you talk to your developer or spot issues yourself.
Before You Start: A Warning About Lazy Loading
Lazy loading is great for images below the fold, but never lazy load your main product image or hero images. These are your “LCP” (Largest Contentful Paint) images—the biggest thing visitors see first.
Data shows that 59% of Shopify stores lazy load their LCP images, and those sites have LCP times about 3 seconds slower than stores that don’t. Only lazy load images that appear after scrolling.
1. Smart Lazy Loading (Not Everything)
Lazy load images that aren’t immediately visible, but eager load your main images.
For images below the fold:
<img
src="{{ product.image | image_url: width: 533 }}"
loading="lazy"
alt="{{ product.image.alt | escape }}"
>
For your main/hero images (don’t lazy load):
<img
src="{{ product.featured_image | image_url: width: 800 }}"
loading="eager"
fetchpriority="high"
alt="{{ product.featured_image.alt | escape }}"
>
Why it matters: Lazy loading everything is a common mistake. Your hero image and main product photo should load immediately.
2. Responsive Images (Right Size for Every Device)
Serve smaller images to mobile phones and larger ones to desktops. This can reduce page weight by 30-50% on image-heavy pages.
<img
srcset="{{ image | image_url: width: 165 }} 165w,
{{ image | image_url: width: 360 }} 360w,
{{ image | image_url: width: 533 }} 533w,
{{ image | image_url: width: 720 }} 720w,
{{ image | image_url }} {{ image.width }}w"
sizes="(min-width: 990px) 50vw, 100vw"
src="{{ image | image_url: width: 533 }}"
alt="{{ image.alt | escape }}"
>
Why it matters: A phone doesn’t need a 2000px image. Serving appropriate sizes saves bandwidth and speeds up loading.
3. The Easiest Way: Use image_tag
Shopify’s image_tag filter automatically handles responsive images and smart lazy loading. It’s the simplest approach.
{{ product.featured_image | image_url: width: 1080 | image_tag }}
This one line generates proper srcset, sizes, and loading attributes automatically. Images in sections beyond the first three automatically get lazy loading.
4. Defer JavaScript Loading
JavaScript that isn’t needed immediately should load after the page renders.
Instead of:
<script src="{{ 'bundle.js' | asset_url }}"></script>
Use:
<script src="{{ 'bundle.js' | asset_url }}" defer></script>
Why it matters: Without defer, the browser stops rendering the page while it downloads and runs JavaScript. With defer, it downloads in the background and runs after the HTML is parsed.
5. Load CSS Only When Needed
Don’t load stylesheets for features that aren’t on the current page.
{%- unless skip_styles -%}
{{ 'component-rating.css' | asset_url | stylesheet_tag }}
{{ 'component-price.css' | asset_url | stylesheet_tag }}
{%- endunless -%}
Why it matters: Every CSS file blocks rendering until it’s downloaded and parsed. Loading only what’s needed reduces this delay.
6. Host Assets on Shopify (Not External CDNs)
External scripts from other domains add connection overhead. Hosting on Shopify is faster.
Avoid:
<script src="https://example.com/script.js"></script>
Better:
<script src="{{ 'script.js' | asset_url }}" defer></script>
Same applies to fonts. Instead of Google Fonts:
<link href="{{ 'custom-font.css' | asset_url }}" rel="stylesheet">
Why it matters: Connecting to external domains takes time. Keeping everything on Shopify’s CDN is faster.
7. Reusable Snippets (Don’t Repeat Code)
Create one snippet for common elements like price displays, then use it everywhere.
{% render 'price',
product: card_product,
price_class: '',
show_compare_at_price: true
%}
Why it matters: Consistent code is easier to maintain and optimise. Fix it once, it’s fixed everywhere.
8. Avoid Loops When You Don’t Need Them
Liquid has built-in filters that are faster than manual loops.
Slow approach:
{% assign total = 0 %}
{% for review in product.reviews %}
{% assign total = total | plus: review.rating %}
{% endfor %}
Faster approach:
{% assign total = product.reviews | map: 'rating' | sum %}
Why it matters: Built-in filters are optimised at the Liquid engine level. Manual loops are slower, especially with large datasets.
9. Conditional Loading Based on Settings
Only load features when they’re enabled in theme settings.
{% if settings.show_reviews %}
{% render 'product-reviews', product: product %}
{% endif %}
Why it matters: If a feature is disabled, there’s no point loading the code for it.
10. Use Section Properties for Smarter Lazy Loading
Shopify provides section index properties that help you decide what to lazy load.
{% if section.index > 2 %}
{% assign loading = 'lazy' %}
{% else %}
{% assign loading = 'eager' %}
{% endif %}
<img src="{{ image | image_url: width: 800 }}" loading="{{ loading }}">
Why it matters: This automatically eager-loads images in your first few sections (visible immediately) and lazy-loads the rest.
Where to Add This Code
If you’re comfortable editing theme files, here’s where to find things:
- Go to Online Store → Themes → Actions → Edit code
- Key folders:
Layout/theme.liquid– global code (scripts, fonts)Sections/– page sections (product info, headers)Snippets/– reusable code piecesAssets/– CSS and JavaScript files
Always back up your theme before making changes. Go to Actions → Duplicate to create a copy first.
Performance Targets to Aim For
Use Google PageSpeed Insights or Lighthouse to check your scores. Aim for:
- Largest Contentful Paint (LCP): Under 2.5 seconds
- First Input Delay (FID): Under 100 milliseconds
- Cumulative Layout Shift (CLS): Under 0.1
- Time to First Byte (TTFB): Under 600ms for well-optimised themes
These snippets can help you hit these targets, but every store is different. Test changes one at a time so you know what’s working.