Blog

Mastering the Liquid `capture` Tag: 5 Creative Use Cases for Cleaner Shopify Themes

Go beyond basic variable assignment and learn advanced techniques for using the Liquid `capture` tag to build complex strings, component slots, and dynamic content blocks.

Mastering the Liquid `capture` Tag: 5 Creative Use Cases for Cleaner Shopify Themes

In the world of Shopify theme development, the assign tag is the undisputed workhorse for creating variables. But when your logic gets more complex, building strings with multiple parts or generating blocks of HTML can lead to messy, hard-to-read code.

This is where the often-underestimated capture tag shines.

While assign is perfect for storing a single value, capture is designed to “capture” the rendered output of an entire block of Liquid code into a variable. This simple difference unlocks a host of powerful techniques that can make your theme code cleaner, more modular, and dramatically more efficient.

Let’s move beyond the basics and explore five creative use cases for mastering the capture tag.


Use Case 1: The Performance-Boosting Loop

This is the classic and most critical use case for capture. As covered in our post on Liquid performance, you should never loop over the same array multiple times to build different parts of a component.

Instead, loop once and use capture to build the HTML for each part in separate variables.

The Slow Way (Multiple Loops):

<!-- Loop 1: Build images -->
<div class="product-gallery">
  {% for variant in product.variants %}
    <img src="{{ variant.image.src | img_url: 'medium' }}">
  {% endfor %}
</div>

<!-- Loop 2: Build swatches -->
<div class="product-swatches">
  {% for variant in product.variants %}
    <span class="swatch"></span>
  {% endfor %}
</div>

The capture Way (Single Loop):

{% liquid
  assign gallery_images = ''
  assign color_swatches = ''
%}

{% for variant in product.variants %}
  {% capture gallery_images %}
    {{ gallery_images }}
    <img src="{{ variant.image.src | img_url: 'medium' }}">
  {% endcapture %}

  {% capture color_swatches %}
    {{ color_swatches }}
    <span class="swatch"></span>
  {% endcapture %}
{% endfor %}

<div class="product-gallery">{{ gallery_images }}</div>
<div class="product-swatches">{{ color_swatches }}</div>

Why it’s better: You’ve cut your server’s workload in half by iterating over the variants array only once, leading to a significant performance improvement on products with many variants.


Use Case 2: Creating “Slots” for Reusable Snippets

Have you ever wanted to pass a block of HTML into a snippet? With capture, you can create a powerful “slot” pattern that makes your reusable components far more flexible.

Imagine a generic card.liquid snippet that needs a unique header and footer for different contexts.

The Snippet (card.liquid):

<div class="card">
  <div class="card-header">
    {{ header_content }}
  </div>
  <div class="card-body">
    Main content here...
  </div>
  <div class="card-footer">
    {{ footer_content }}
  </div>
</div>

Using capture to Fill the Slots:

{% capture card_header %}
  <h2>Special Product Title</h2>
  <p>With a unique subtitle</p>
{% endcapture %}

{% capture card_footer %}
  <a href="#" class="btn">Buy Now</a>
  <a href="#" class="link">Learn More</a>
{% endcapture %}

{% render 'card',
  header_content: card_header,
  footer_content: card_footer
%}

Why it’s better: Your card.liquid snippet remains clean and generic, while you retain full control over the complex HTML that gets passed into it. This is far superior to passing a dozen individual string variables.


Use Case 3: Conditionally Building CSS Class Strings

Building a dynamic string of CSS classes can get messy with multiple if statements. capture allows you to build the string in a more readable and organized way.

{% capture product_card_classes %}
  product-card
  {% if product.available == false %}product-card--sold-out{% endif %}
  {% if product.compare_at_price > product.price %}product-card--on-sale{% endif %}
  {% if product.tags contains 'new' %}product-card--new-arrival{% endif %}
{% endcapture %}

<div class="{{ product_card_classes | strip | strip_newlines | replace: '  ', ' ' }}">
  ...
</div>

Why it’s better: The logic for adding each class is cleanly separated onto its own line, making it much easier to read and maintain than a series of chained append filters.


Use Case 4: Generating Complex JSON-LD for SEO

Writing JSON-LD for rich snippets can be a nightmare of escaped quotes and complex string concatenation. capture simplifies this by allowing you to write the JSON structure in a more natural way.

{% capture product_json_ld %}
  {
    "@context": "https://schema.org/",
    "@type": "Product",
    "name": "{{ product.title | escape }}",
    "image": "{{ product.featured_image | img_url: '1024x1024' }}",
    "description": "{{ product.description | strip_html | escape }}",
    "sku": "{{ product.selected_or_first_available_variant.sku }}",
    "offers": {
      "@type": "Offer",
      "url": "{{ request.origin }}{{ product.url }}",
      "priceCurrency": "{{ cart.currency.iso_code }}",
      "price": "{{ product.price | money_without_currency | replace: ',', '' }}"
    }
  }
{% endcapture %}

<script type="application/ld+json">
  {{ product_json_ld }}
</script>

Why it’s better: You can write the JSON object with proper formatting and indentation, making it vastly easier to debug than a single, long, concatenated string.


Use Case 5: Creating Fallback Chains for Content

Sometimes you want to display a piece of content from one of several possible sources. capture combined with strip provides an elegant way to handle this.

{% capture image_alt_text %}
  {{ product.featured_image.alt | strip }}
{% endcapture %}

{% if image_alt_text == blank %}
  {% capture image_alt_text %}
    {{ product.title | strip }}
  {% endcapture %}
{% endif %}

<img src="..." alt="{{ image_alt_text }}">

Why it’s better: This pattern is cleaner than nested if/else statements and ensures you always have a non-empty value for critical attributes like alt text.


Final Thoughts: A Powerful Tool for Clean Code

The capture tag is more than just a multi-line assign. It’s a powerful tool for improving performance, creating flexible components, and writing cleaner, more maintainable Liquid code.

The next time you find yourself building a complex string or block of HTML, reach for capture. It will transform the way you write Liquid.

❓ What’s your favorite creative use for the capture tag?