Claude Code Plugins

Community-maintained marketplace

Feedback

Core Shopify Liquid templating best practices for performance, maintainability, and clean code

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name Shopify Liquid Fundamentals
description Core Shopify Liquid templating best practices for performance, maintainability, and clean code

Shopify Liquid Fundamentals

Core Principles

Whitespace Control

  • Use {%- -%} to trim whitespace around Liquid tags
  • Prefer {% render %} over deprecated {% include %}
  • Use {% liquid %} for multi-line logic blocks

Example:

{%- liquid
  assign product_available = product.available | default: false
  assign product_price = product.price | default: 0

  if product == blank
    assign error_message = 'Product not found'
  endif
-%}

Performance Optimization

  • Minimize Liquid logic in templates
  • Use assign for complex calculations instead of inline logic
  • Cache expensive operations
  • Use snippets for reusable code

Good:

{%- assign discounted_price = product.price | times: 0.9 | money -%}
<p>Sale: {{ discounted_price }}</p>

Bad:

<p>Sale: {{ product.price | times: 0.9 | money }}</p>
<p>Save: {{ product.price | times: 0.1 | money }}</p>

Error Handling

Always provide defaults and handle empty states:

{%- liquid
  assign heading = section.settings.heading | default: 'Default Heading'
  assign show_vendor = section.settings.show_vendor | default: false

  if product == blank
    assign error_message = 'Product unavailable'
  endif
-%}

{%- if error_message -%}
  <p class="error">{{ error_message }}</p>
{%- else -%}
  <!-- Normal content -->
{%- endif -%}

Liquid Syntax Essentials

Output

{{ variable }}              # Output variable
{{ variable | escape }}     # Escape HTML
{{ variable | default: 'fallback' }}  # Provide default
{{- variable -}}            # Trim whitespace

Logic

{% if condition %}
{% elsif other_condition %}
{% else %}
{% endif %}

{% unless condition %}
{% endunless %}

{% case variable %}
  {% when 'value1' %}
  {% when 'value2' %}
  {% else %}
{% endcase %}

Loops

{% for item in collection %}
  {{ item.title }}
{% endfor %}

{% for item in collection limit: 4 %}
  {{ item.title }}
{% endfor %}

{% for i in (1..5) %}
  Item {{ i }}
{% endfor %}

Variables

{% assign name = 'value' %}
{% capture variable %}
  Content here
{% endcapture %}

Common Filters

String Filters

{{ 'hello' | capitalize }}           # Hello
{{ 'HELLO' | downcase }}            # hello
{{ 'hello' | upcase }}              # HELLO
{{ 'hello world' | truncate: 8 }}   # hello...
{{ '<p>test</p>' | escape }}        # &lt;p&gt;test&lt;/p&gt;
{{ 'hello world' | remove: 'world' }} # hello
{{ 'hello' | append: ' world' }}    # hello world

Array Filters

{{ collection | size }}              # Number of items
{{ collection | first }}             # First item
{{ collection | last }}              # Last item
{{ collection | join: ', ' }}        # Join with comma
{{ collection | reverse }}           # Reverse order
{{ collection | sort: 'title' }}     # Sort by property

Math Filters

{{ 10 | plus: 5 }}                  # 15
{{ 10 | minus: 5 }}                 # 5
{{ 10 | times: 5 }}                 # 50
{{ 10 | divided_by: 5 }}            # 2
{{ 10.5 | ceil }}                   # 11
{{ 10.5 | floor }}                  # 10
{{ 10.5 | round }}                  # 11

Money Filters

{{ 1000 | money }}                  # $10.00
{{ 1000 | money_with_currency }}    # $10.00 USD
{{ 1000 | money_without_currency }} # 10.00

Image Filters

{{ image | image_url: width: 400 }}
{{ image | image_url: width: 400, height: 400 }}
{{ image | image_tag }}
{{ image | image_tag: alt: 'Description' }}

Shopify Objects

Global Objects

{{ shop.name }}                     # Store name
{{ shop.email }}                    # Store email
{{ cart.item_count }}               # Cart items
{{ customer.name }}                 # Customer name (if logged in)
{{ settings.color_primary }}        # Theme setting

Product Objects

{{ product.title }}
{{ product.price }}
{{ product.compare_at_price }}
{{ product.available }}
{{ product.vendor }}
{{ product.type }}
{{ product.featured_image }}
{{ product.url }}

Collection Objects

{{ collection.title }}
{{ collection.description }}
{{ collection.products }}
{{ collection.products_count }}
{{ collection.url }}

Best Practices

1. Always Escape User Input

<h1>{{ product.title | escape }}</h1>
<p>{{ customer.name | escape }}</p>

2. Provide Defaults

{%- assign heading = section.settings.heading | default: 'Default Title' -%}
{%- assign image = product.featured_image | default: blank -%}

3. Check for Blank Values

{%- if heading != blank -%}
  <h2>{{ heading | escape }}</h2>
{%- endif -%}

4. Use Liquid Tag for Multi-line Logic

{%- liquid
  assign is_sale = false
  if product.compare_at_price > product.price
    assign is_sale = true
  endif

  assign discount_percent = product.compare_at_price | minus: product.price | times: 100 | divided_by: product.compare_at_price
-%}

5. Minimize Nested Conditions

{%- # Bad -%}
{% if product.available %}
  {% if product.compare_at_price > product.price %}
    <span>On Sale</span>
  {% endif %}
{% endif %}

{%- # Good -%}
{%- liquid
  assign is_on_sale = false
  if product.available and product.compare_at_price > product.price
    assign is_on_sale = true
  endif
-%}

{%- if is_on_sale -%}
  <span>On Sale</span>
{%- endif -%}

Common Patterns

Conditional Class Names

<div class="product{% if product.available %} in-stock{% else %} out-of-stock{% endif %}">
  <!-- Content -->
</div>

Loop with Index

{%- for product in collection.products -%}
  <div class="product-{{ forloop.index }}">
    {%- if forloop.first -%}
      <span>Featured</span>
    {%- endif -%}
    {{ product.title }}
  </div>
{%- endfor -%}

Empty State Handling

{%- if collection.products.size > 0 -%}
  {%- for product in collection.products -%}
    <!-- Product markup -->
  {%- endfor -%}
{%- else -%}
  <p>No products available.</p>
{%- endif -%}

Responsive Images

{%- if product.featured_image -%}
  <img
    srcset="
      {{ product.featured_image | image_url: width: 400 }} 400w,
      {{ product.featured_image | image_url: width: 800 }} 800w,
      {{ product.featured_image | image_url: width: 1200 }} 1200w
    "
    sizes="(min-width: 1024px) 33vw, (min-width: 768px) 50vw, 100vw"
    src="{{ product.featured_image | image_url: width: 800 }}"
    alt="{{ product.featured_image.alt | escape }}"
    loading="lazy"
    width="800"
    height="{{ 800 | divided_by: product.featured_image.aspect_ratio | ceil }}"
  >
{%- endif -%}

Rendering Snippets

Basic Rendering

{% render 'product-card' %}

With Parameters

{% render 'product-card', product: product, show_vendor: true %}

With Multiple Parameters

{% render 'button',
  text: 'Add to Cart',
  url: product.url,
  style: 'primary',
  size: 'large'
%}

Performance Tips

  1. Limit loops - Use limit parameter when possible
  2. Cache expensive operations - Assign to variables
  3. Minimize API calls - Don't call same object property multiple times
  4. Use snippets wisely - Balance between reusability and overhead
  5. Optimize images - Use appropriate sizes with image filters

Common Mistakes to Avoid

Don't repeat expensive operations:

<p>{{ product.price | times: 1.1 | money }}</p>
<p>{{ product.price | times: 1.1 | money }}</p>

Do assign to variable:

{%- assign price_with_tax = product.price | times: 1.1 | money -%}
<p>{{ price_with_tax }}</p>
<p>{{ price_with_tax }}</p>

Don't forget to escape:

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

Do escape user input:

<h1>{{ product.title | escape }}</h1>

Don't use deprecated include:

{% include 'snippet-name' %}

Do use render:

{% render 'snippet-name' %}

Follow these fundamentals for clean, performant, maintainable Shopify Liquid code.