Shopify Liquid Complete Guide: Performance & Best Practices

What is Liquid? Core Understanding

Shopify Liquid is the templating language used for Shopify theme development. It embeds dynamic content into HTML, making it essential for displaying product information and cart contents.

However, poor Liquid implementation can drastically slow down your store. This article shares best practices for performance-focused Liquid coding, based on real-world development experience.

5 Anti-Patterns That Kill Performance

1. Object Fetching Inside Loops

{% raw %}
<!-- ❌ BAD: Repeatedly fetching product objects in loop -->
{% for item in cart.items %}
  {% assign product = all_products[item.product.handle] %}
  {{ product.title }}
{% endfor %}

<!-- ✅ GOOD: Access data directly from item -->
{% for item in cart.items %}
  {{ item.product.title }}
{% endfor %}
{% endraw %}

Impact: With 10 cart items, this triggers 10 database queries. Page render time can increase by 2-3 seconds.

2. Unnecessary Full Collection Fetching

{% raw %}
<!-- ❌ BAD: Fetch all products then filter -->
{% assign featured_products = collections.all.products | where: "tags", "featured" %}

<!-- ✅ GOOD: Use dedicated collection -->
{% assign featured_products = collections.featured.products %}
{% endraw %}

3. Deeply Nested Loops

{% raw %}
<!-- ❌ BAD: Triple nested loop = O(n³) complexity -->
{% for collection in collections %}
  {% for product in collection.products %}
    {% for variant in product.variants %}
      <!-- Processing -->
    {% endfor %}
  {% endfor %}
{% endfor %}

<!-- ✅ GOOD: Split logic, use pagination -->
{% paginate collection.products by 20 %}
  {% for product in paginate.collection.products %}
    <!-- Processing -->
  {% endfor %}
{% endpaginate %}
{% endraw %}

4. Missing Image Size Specifications

{% raw %}
<!-- ❌ BAD: Loading original size -->
<img src="{{ product.featured_image }}" alt="{{ product.title }}">

<!-- ✅ GOOD: Specify appropriate sizes -->
<img
  src="{{ product.featured_image | image_url: width: 600 }}"
  srcset="{{ product.featured_image | image_url: width: 600 }} 1x,
          {{ product.featured_image | image_url: width: 1200 }} 2x"
  alt="{{ product.title }}"
  loading="lazy"
>
{% endraw %}

5. Excessive Conditional Logic

{% raw %}
<!-- ❌ BAD: Repeatedly checking same condition -->
{% if product.available %}
  <button>Add to Cart</button>
{% endif %}
{% if product.available %}
  <span>In Stock</span>
{% endif %}

<!-- ✅ GOOD: Evaluate once with assign -->
{% assign is_available = product.available %}
{% if is_available %}
  <button>Add to Cart</button>
  <span>In Stock</span>
{% endif %}
{% endraw %}

Performance Optimization Techniques

Implementing Lazy Loading

{% raw %}
<!-- Above the fold -->
<img src="{{ section.settings.hero_image | image_url: width: 1200 }}" alt="Hero">

<!-- Below the fold -->
<img
  src="{{ product.image | image_url: width: 400 }}"
  loading="lazy"
  alt="{{ product.title }}"
>
{% endraw %}

Cache-Friendly Code

{% raw %}
<!-- ✅ GOOD: Static data is cacheable -->
<div data-product-id="{{ product.id }}">
  {{ product.title }}
</div>

<!-- ❌ BAD: Cart-dependent, not cacheable -->
<div>
  Items in cart: {{ cart.item_count }}
</div>
<!-- Use JavaScript for dynamic fetching instead -->
{% endraw %}

Leveraging Metafields for Flexibility

{% raw %}
<!-- Store custom data in metafields -->
{% if product.metafields.custom.badge %}
  <span class="badge">{{ product.metafields.custom.badge }}</span>
{% endif %}
{% endraw %}

Debugging Techniques

Inspecting Liquid Variables

{% raw %}
<!-- Display only in development -->
{% if shop.name contains 'Development' %}
  <pre>{{ product | json }}</pre>
{% endif %}
{% endraw %}

Performance Measurement

{% raw %}
{% assign start_time = 'now' | date: '%s' %}
<!-- Heavy processing -->
{% assign end_time = 'now' | date: '%s' %}
<!-- Console: {{ end_time | minus: start_time }} seconds -->
{% endraw %}

Implementation Checklist

Verify before deployment:

  • Loops limited to 3 levels deep?
  • Images have width/height attributes?
  • Using loading=“lazy” below the fold?
  • No unnecessary all_products access?
  • Customizable design via metafields?
  • Mobile speed verified with Lighthouse?

Conclusion

Liquid is powerful, but poor usage means store death. Follow these 3 critical rules:

  1. Avoid data fetching inside loops
  2. Always optimize images
  3. Minimize conditional logic

DEMETIO offers Liquid performance audit services. Contact us for existing store speed improvements.


Related Articles: