Shopify Liquid Code Examples

Real-world, production-ready Shopify Liquid code examples organized by use case. Every snippet below is copy-paste ready for your theme. New to Liquid? Start with our beginner's guide or grab the cheat sheet for quick reference.

Product Page Examples

1. Product Card with Sale Badge

A complete product card showing the featured image, title, price, and a sale badge when the item is on sale.

liquid
<div class="product-card">
  <a href="{{ product.url }}">
    <img src="{{ product.featured_image | img_url: '400x' }}"
         alt="{{ product.featured_image.alt | escape }}"
         loading="lazy">
  </a>

  {% if product.compare_at_price > product.price %}
    <span class="sale-badge">Sale</span>
  {% endif %}

  <h3>{{ product.title }}</h3>

  {% if product.compare_at_price > product.price %}
    <s>{{ product.compare_at_price | money }}</s>
    <strong>{{ product.price | money }}</strong>
  {% else %}
    <span>{{ product.price | money }}</span>
  {% endif %}
</div>

How it works: compare_at_price is the original price before a discount. The conditional checks if a sale is active and shows both the struck-through original and the new price. The money filter formats the price in the store's currency.

2. Variant Selector Dropdown

A size/color variant selector that updates the add-to-cart form when the customer picks an option.

liquid
{% if product.variants.size > 1 %}
  <select name="id" id="variant-selector">
    {% for variant in product.variants %}
      <option
        value="{{ variant.id }}"
        {% if variant == product.selected_or_first_available_variant %}
          selected="selected"
        {% endif %}
        {% unless variant.available %}
          disabled="disabled"
        {% endunless %}
      >
        {{ variant.title }}{{ variant.price | money }}
        {% unless variant.available %} (Sold out){% endunless %}
      </option>
    {% endfor %}
  </select>
{% else %}
  <input type="hidden" name="id" value="{{ product.variants.first.id }}">
{% endif %}

How it works: Loops through all variants, pre-selects the first available one, and disables sold-out options. If there's only one variant, it uses a hidden input instead.

3. Product Image Gallery

A thumbnail gallery that displays all product images with a main image and clickable thumbnails.

liquid
<div class="product-gallery">
  <!-- Main image -->
  <div class="main-image">
    <img id="main-product-image"
         src="{{ product.featured_image | img_url: '800x' }}"
         alt="{{ product.featured_image.alt | escape }}">
  </div>

  <!-- Thumbnails -->
  {% if product.images.size > 1 %}
    <div class="thumbnails">
      {% for image in product.images %}
        <button data-full="{{ image | img_url: '800x' }}">
          <img src="{{ image | img_url: '100x' }}"
               alt="{{ image.alt | escape }}">
        </button>
      {% endfor %}
    </div>
  {% endif %}
</div>

How it works: The main image loads at 800px width. Thumbnails load at 100px. The data-full attribute stores the full-size URL — use JavaScript to swap the main image on click.

4. Product Metafields Display

Display custom metafield data like materials, dimensions, or care instructions on the product page.

liquid
<!-- Access product metafields -->
{% assign material = product.metafields.custom.material %}
{% assign care = product.metafields.custom.care_instructions %}
{% assign dimensions = product.metafields.custom.dimensions %}

{% if material or care or dimensions %}
  <div class="product-details">
    <h4>Product Details</h4>
    <dl>
      {% if material %}
        <dt>Material</dt>
        <dd>{{ material.value }}</dd>
      {% endif %}
      {% if dimensions %}
        <dt>Dimensions</dt>
        <dd>{{ dimensions.value }}</dd>
      {% endif %}
      {% if care %}
        <dt>Care</dt>
        <dd>{{ care.value }}</dd>
      {% endif %}
    </dl>
  </div>
{% endif %}

How it works: Metafields are accessed via product.metafields.NAMESPACE.KEY. The outer conditional ensures the section only renders if at least one metafield has a value.

Collection Page Examples

5. Collection Grid with Pagination

A paginated product grid that displays 12 products per page with previous/next navigation.

liquid
{% paginate collection.products by 12 %}
  <div class="product-grid">
    {% for product in collection.products %}
      <div class="grid-item">
        <a href="{{ product.url | within: collection }}">
          <img src="{{ product.featured_image | img_url: '300x' }}"
               alt="{{ product.title | escape }}" loading="lazy">
          <h3>{{ product.title }}</h3>
          <p>{{ product.price | money }}</p>
        </a>
      </div>
    {% endfor %}
  </div>

  {% if paginate.pages > 1 %}
    <nav class="pagination">
      {% if paginate.previous %}
        <a href="{{ paginate.previous.url }}">&larr; Previous</a>
      {% endif %}
      <span>Page {{ paginate.current_page }} of {{ paginate.pages }}</span>
      {% if paginate.next %}
        <a href="{{ paginate.next.url }}">Next &rarr;</a>
      {% endif %}
    </nav>
  {% endif %}
{% endpaginate %}

How it works: The paginate tag splits products into pages. The within filter on product URLs ensures the “Back” button returns customers to this collection. Learn more about loops in Liquid.

6. Filter Products by Tag

Tag-based filter buttons that let customers narrow down products in a collection.

liquid
<div class="tag-filters">
  <a href="{{ collection.url }}"
     class="{% unless current_tags %}active{% endunless %}">
    All
  </a>
  {% for tag in collection.all_tags %}
    {% if current_tags contains tag %}
      <a href="{{ tag | link_to_remove_tag: tag }}" class="active">
        {{ tag }} &times;
      </a>
    {% else %}
      <a href="{{ tag | link_to_tag: tag }}">
        {{ tag }}
      </a>
    {% endif %}
  {% endfor %}
</div>

How it works: collection.all_tags gives every tag in the collection. Active tags show a remove button (×), inactive tags link to filter.

7. Sort Products by Price

A dropdown that lets customers sort products by price, name, or date.

liquid
<form>
  <label for="sort-by">Sort by:</label>
  <select id="sort-by" onchange="window.location = this.value">
    <option value="{{ collection.url }}?sort_by=manual">Featured</option>
    <option value="{{ collection.url }}?sort_by=price-ascending">Price: Low to High</option>
    <option value="{{ collection.url }}?sort_by=price-descending">Price: High to Low</option>
    <option value="{{ collection.url }}?sort_by=title-ascending">A &ndash; Z</option>
    <option value="{{ collection.url }}?sort_by=created-descending">Newest</option>
    <option value="{{ collection.url }}?sort_by=best-selling">Best Selling</option>
  </select>
</form>

How it works: Shopify's built-in sort_by parameter handles sorting server-side. The onchange redirects to the same collection with the selected sort order.

Cart Examples

8. Cart Line Items with Totals

A complete cart display showing each item with image, title, variant, quantity, line price, and the order total.

liquid
{% if cart.item_count > 0 %}
  <form action="/cart" method="post">
    {% for item in cart.items %}
      <div class="cart-item">
        <img src="{{ item.image | img_url: '120x' }}"
             alt="{{ item.title | escape }}">
        <div>
          <h4>{{ item.product.title }}</h4>
          {% unless item.variant.title == "Default Title" %}
            <p>{{ item.variant.title }}</p>
          {% endunless %}
          <p>{{ item.price | money }} &times; {{ item.quantity }}</p>
        </div>
        <span>{{ item.line_price | money }}</span>
      </div>
    {% endfor %}

    <div class="cart-total">
      <strong>Subtotal: {{ cart.total_price | money }}</strong>
    </div>
    <button type="submit" name="checkout">Checkout</button>
  </form>
{% else %}
  <p>Your cart is empty. <a href="/collections/all">Continue shopping</a></p>
{% endif %}

9. Free Shipping Progress Bar

A visual progress bar showing how close the customer is to free shipping.

liquid
{% assign threshold = 5000 %} <!-- $50.00 in cents -->
{% assign remaining = threshold | minus: cart.total_price %}
{% assign progress = cart.total_price | times: 100 | divided_by: threshold %}
{% if progress > 100 %}{% assign progress = 100 %}{% endif %}

<div class="shipping-bar">
  {% if remaining <= 0 %}
    <p>&#x2705; You qualify for free shipping!</p>
  {% else %}
    <p>Add {{ remaining | money }} more for free shipping</p>
  {% endif %}
  <div class="bar-track">
    <div class="bar-fill" style="width: {{ progress }}%"></div>
  </div>
</div>

How it works: Prices in Liquid are in cents, so $50 = 5000. The math filters calculate the remaining amount and percentage for the progress bar width.

Navigation Examples

10. Multi-Level Dropdown Navigation

A header navigation with dropdown sub-menus built from Shopify's navigation menus.

liquid
<nav class="main-nav">
  {% for link in linklists.main-menu.links %}
    <div class="nav-item">
      <a href="{{ link.url }}"
         {% if link.active %}class="active"{% endif %}
         {% if link.links.size > 0 %}aria-haspopup="true"{% endif %}>
        {{ link.title }}
      </a>
      {% if link.links.size > 0 %}
        <ul class="dropdown">
          {% for child in link.links %}
            <li><a href="{{ child.url }}">{{ child.title }}</a></li>
          {% endfor %}
        </ul>
      {% endif %}
    </div>
  {% endfor %}
</nav>

11. Breadcrumb Navigation

Breadcrumbs that show the current page hierarchy — great for SEO and user orientation.

liquid
<nav aria-label="Breadcrumb">
  <ol class="breadcrumbs">
    <li><a href="/">Home</a></li>

    {% if template contains "product" %}
      {% if collection %}
        <li><a href="{{ collection.url }}">{{ collection.title }}</a></li>
      {% endif %}
      <li aria-current="page">{{ product.title }}</li>

    {% elsif template contains "collection" %}
      <li aria-current="page">{{ collection.title }}</li>

    {% elsif template contains "article" %}
      <li><a href="{{ blog.url }}">{{ blog.title }}</a></li>
      <li aria-current="page">{{ article.title }}</li>

    {% elsif template contains "page" %}
      <li aria-current="page">{{ page.title }}</li>
    {% endif %}
  </ol>
</nav>

Blog Examples

12. Blog Post Listing with Excerpts

A blog index page showing posts with featured images, excerpts, and read more links.

liquid
{% for article in blog.articles limit: 6 %}
  <article class="blog-card">
    {% if article.image %}
      <a href="{{ article.url }}">
        <img src="{{ article.image | img_url: '600x300' }}"
             alt="{{ article.image.alt | escape }}" loading="lazy">
      </a>
    {% endif %}
    <h2><a href="{{ article.url }}">{{ article.title }}</a></h2>
    <time datetime="{{ article.published_at | date: '%F' }}">
      {{ article.published_at | date: '%B %d, %Y' }}
    </time>
    <p>{{ article.excerpt_or_content | strip_html | truncatewords: 30 }}</p>
    <a href="{{ article.url }}">Read more &rarr;</a>
  </article>
{% endfor %}

13. Related Articles by Tag

Show related blog posts at the bottom of an article based on shared tags.

liquid
{% assign current_tags = article.tags %}
{% assign related_count = 0 %}

<div class="related-posts">
  <h3>Related Articles</h3>
  {% for post in blog.articles %}
    {% if post.id != article.id and related_count < 3 %}
      {% for tag in post.tags %}
        {% if current_tags contains tag %}
          <a href="{{ post.url }}">{{ post.title }}</a>
          {% assign related_count = related_count | plus: 1 %}
          {% break %}
        {% endif %}
      {% endfor %}
    {% endif %}
  {% endfor %}
</div>

Customer Account Examples

14. Order History Table

A customer account page showing their past orders with status, date, and total.

liquid
{% if customer.orders.size > 0 %}
  <table class="order-history">
    <thead>
      <tr>
        <th>Order</th>
        <th>Date</th>
        <th>Status</th>
        <th>Total</th>
      </tr>
    </thead>
    <tbody>
      {% for order in customer.orders %}
        <tr>
          <td>
            <a href="{{ order.customer_url }}">{{ order.name }}</a>
          </td>
          <td>{{ order.created_at | date: '%B %d, %Y' }}</td>
          <td>{{ order.fulfillment_status_label }}</td>
          <td>{{ order.total_price | money }}</td>
        </tr>
      {% endfor %}
    </tbody>
  </table>
{% else %}
  <p>You haven&apos;t placed any orders yet.</p>
{% endif %}

15. Customer Address Form

A form for customers to add or edit a shipping address in their account.

liquid
{% form 'customer_address', customer.new_address %}
  <div class="address-form">
    <label for="first_name">First Name</label>
    <input type="text" id="first_name" name="address[first_name]"
           value="{{ form.first_name }}">

    <label for="last_name">Last Name</label>
    <input type="text" id="last_name" name="address[last_name]"
           value="{{ form.last_name }}">

    <label for="address1">Address</label>
    <input type="text" id="address1" name="address[address1]"
           value="{{ form.address1 }}">

    <label for="city">City</label>
    <input type="text" id="city" name="address[city]"
           value="{{ form.city }}">

    <label for="country">Country</label>
    <select id="country" name="address[country]">
      {{ country_option_tags }}
    </select>

    <label for="zip">ZIP / Postal Code</label>
    <input type="text" id="zip" name="address[zip]"
           value="{{ form.zip }}">

    <button type="submit">Save Address</button>
  </div>
{% endform %}

How it works: The form tag generates the correct action URL and CSRF token.country_option_tags outputs all country options with the saved country pre-selected.

Related Guides

We're building an AI Liquid code generator — join the waitlist.

Get early access when we launch. No spam, just one email.