Files
gh-sarojpunde-shopify-dev-t…/skills/shopify-liquid-fundamentals/SKILL.md
2025-11-30 08:54:05 +08:00

8.1 KiB

name, description
name description
Shopify Liquid Fundamentals 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.