TL;DR

Liquid is NOT purely presentational, but its logic is intentionally constrained. It supports:

  • Conditionals (if/else/unless)
  • Loops (for)
  • Variable assignment (assign, capture)
  • Filters (transformations)

It prohibits:

  • Custom functions
  • State management
  • Direct API calls
  • Complex algorithms/recursion
  • Backend logic access

Design Philosophy

Liquid was designed with a specific security model in mind:

“Liquid needs to be non-evaling and secure. Liquid templates are made so that users can edit them. You don’t want to run code on your server which your users wrote.” — Shopify/liquid GitHub

Core Principles

  1. Non-evaluating: No arbitrary code execution
  2. Stateless: No concept of persistent state between renders
  3. Sandboxed: Users cannot access backend systems
  4. Separated compile/render: Expensive parsing done once, then render with passed data

Logic Capabilities

What Liquid CAN Do

FeatureSyntaxPurpose
Conditionals{% if %}, {% unless %}, {% case %}Control flow
Loops{% for item in collection %}Iteration
Variables{% assign x = value %}Local assignment
Capture{% capture var %}...{% endcapture %}String building
Filters{{ value | upcase | truncate: 20 }}Transformations

Logic Example

{% if product.available %}
{% for variant in product.variants %}
{% if variant.inventory_quantity > 0 %}
<option value="{{ variant.id }}">
{{ variant.title }} - {{ variant.price | money }}
</option>
{% endif %}
{% endfor %}
{% else %}
<p>Out of stock</p>
{% endif %}

This demonstrates real logic — conditionals, loops, comparisons — not just variable interpolation.

Limitations (By Design)

No Custom Functions

Liquid only provides predefined tags and filters. You cannot define:

{% comment %} NOT POSSIBLE {% endcomment %}
{% function calculate_discount(price, percent) %}
...
{% endfunction %}

No State

Each render is isolated. No persistence between requests:

{% comment %} NOT POSSIBLE {% endcomment %}
{% increment_global_counter %}
{% remember user_preference %}

No API Calls

Liquid cannot fetch external data:

{% comment %} NOT POSSIBLE {% endcomment %}
{% fetch 'https://api.example.com/data' %}

Data fetching is a JavaScript task or must come from Shopify’s backend.

Limited Operators

  • No parentheses for grouping: {% if (a and b) or c %} is invalid
  • Operators evaluated right-to-left with and/or
  • Cannot check for objects in arrays of objects with contains

No Complex Algorithms

No recursion, no while loops, limited mathematical operations.

Where Business Logic Lives

┌─────────────────────────────────────────────────────────┐
│ Shopify Backend │
│ (Ruby/GraphQL - Real business logic) │
│ • Inventory management │
│ • Payment processing │
│ • Discount calculations │
│ • Order workflows │
└─────────────────────────────────────────────────────────┘
▼ (provides data)
┌─────────────────────────────────────────────────────────┐
│ Liquid Templates │
│ (Presentation + Display Logic) │
│ • Render product info │
│ • Show/hide elements conditionally │
│ • Format prices, dates │
│ • Loop through collections │
└─────────────────────────────────────────────────────────┘
▼ (outputs HTML)
┌─────────────────────────────────────────────────────────┐
│ Browser/JavaScript │
│ (Client-side interactivity) │
│ • Event handling │
│ • Animations │
│ • AJAX/API calls │
│ • Dynamic updates │
└─────────────────────────────────────────────────────────┘

Security Model

Liquid’s constraints are its security features:

ConstraintSecurity Benefit
No evalPrevents code injection
No file accessPrevents data exfiltration
No network accessPrevents SSRF attacks
StatelessPrevents session hijacking
Limited operationsPrevents DoS via computation

This allows Shopify to safely let merchants edit templates without risking server security.

Comparison with Other Template Languages

LanguageUser-SafeLogicStateFunctions
LiquidYesLimitedNoNo
Jinja2With sandboxFullYesYes
ERBNoFullYesYes
MustacheYesMinimalNoNo
HandlebarsPartialLimitedNoHelpers
NunjucksNoFullYesYes

Liquid and Mustache are designed for untrusted users. Others require trusted template authors.

Practical Implications

What This Means for Development

  1. Theme development: Liquid handles presentation layer effectively
  2. Complex logic: Must be in Shopify Functions, Apps, or JavaScript
  3. Data transformation: Use filters, but heavy lifting done server-side
  4. Performance: Complex Liquid loops can slow rendering at scale

When Liquid Falls Short

For these use cases, you need Shopify Apps or Functions:

  • Custom pricing logic
  • Complex discount rules
  • External API integrations
  • Real-time inventory from third parties
  • Custom checkout modifications

Conclusion

Liquid occupies a middle ground:

  • More than a simple interpolation engine (has real logic)
  • Less than a general-purpose programming language (intentionally limited)

It’s templating with guardrails — designed for safe, user-editable presentation with enough logic to handle display decisions, but not enough to become a security liability.

Sources

  1. Shopify Liquid Reference
  2. Shopify/liquid GitHub Repository
  3. Liquid Introduction
  4. An Overview of Liquid - Shopify Partners
  5. Understanding Liquid - Praella
  6. Shopify Liquid Guide 2025
  7. Mastering Shopify Liquid Performance - Medium