CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-liquidjs

A simple, expressive and safe Shopify / Github Pages compatible template engine in pure JavaScript.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

built-in-tags.mddocs/

Built-in Tags

LiquidJS provides 21 built-in tags that offer control flow, template composition, variable manipulation, and content processing capabilities. Tags use the {% %} syntax and can contain complex logic and nested structures.

Categories

Tags are organized into the following functional categories:

  • Control Flow: Conditional logic and loops
  • Variable Manipulation: Assignment and modification of variables
  • Template Composition: Including and rendering other templates
  • Content Processing: Capturing and processing content
  • Utility Tags: Comments, raw content, and special behaviors

Control Flow Tags

Tags for implementing conditional logic and iteration.

If/Unless Conditionals

/**
 * Conditional execution based on expression truthiness
 * Supports elsif and else branches
 */
{% if condition %}
  <!-- content when true -->
{% elsif other_condition %}
  <!-- content for elsif -->
{% else %}
  <!-- fallback content -->
{% endif %}

/**
 * Inverse conditional - executes when condition is false
 */
{% unless condition %}
  <!-- content when false -->
{% endunless %}

Usage Examples:

{% if user.active %}
  <p>Welcome back, {{ user.name }}!</p>
{% elsif user.pending %}
  <p>Your account is pending approval.</p>
{% else %}
  <p>Please log in to continue.</p>
{% endif %}

{% unless product.sold_out %}
  <button>Add to Cart</button>
{% endunless %}

<!-- Complex conditions -->
{% if user.age >= 18 and user.verified %}
  <p>Full access granted</p>
{% endif %}

For Loops

/**
 * Iterate over arrays, objects, and ranges
 * Supports modifiers: offset, limit, reversed
 * Provides forloop object with iteration details
 */
{% for variable in collection %}
  <!-- iteration content -->
{% else %}
  <!-- content when collection is empty -->
{% endfor %}

/**
 * For loop modifiers
 */
{% for item in items limit: 5 offset: 2 reversed %}
  {{ item }}
{% endfor %}

/**
 * Forloop object properties:
 * - first: true if first iteration
 * - last: true if last iteration
 * - index: 1-based iteration count
 * - index0: 0-based iteration count
 * - rindex: reverse index (countdown)
 * - rindex0: reverse index starting from 0
 * - length: total iterations
 */

Usage Examples:

<!-- Basic iteration -->
{% for product in products %}
  <div class="product">
    <h3>{{ product.name }}</h3>
    <p>${{ product.price }}</p>
  </div>
{% else %}
  <p>No products available</p>
{% endfor %}

<!-- With forloop object -->
{% for item in items %}
  <div class="item {% if forloop.first %}first{% endif %}">
    {{ forloop.index }}. {{ item.title }}
  </div>
{% endfor %}

<!-- Range iteration -->
{% for i in (1..5) %}
  <span>{{ i }}</span>
{% endfor %}

<!-- Object iteration -->
{% for key_value in user %}
  <p>{{ key_value[0] }}: {{ key_value[1] }}</p>
{% endfor %}

<!-- With modifiers -->
{% for post in posts limit: 10 offset: 5 reversed %}
  <article>{{ post.title }}</article>
{% endfor %}

Case/When Switch

/**
 * Multi-branch conditional similar to switch/case
 * Supports multiple values per when clause
 */
{% case variable %}
  {% when value1 %}
    <!-- content for value1 -->
  {% when value2, value3 %}
    <!-- content for value2 or value3 -->
  {% else %}
    <!-- default content -->
{% endcase %}

Usage Examples:

{% case user.role %}
  {% when 'admin' %}
    <p>Administrator Dashboard</p>
  {% when 'editor', 'author' %}
    <p>Content Management</p>
  {% when 'subscriber' %}
    <p>Read-only Access</p>
  {% else %}
    <p>Guest Access</p>
{% endcase %}

{% case product.status %}
  {% when 'available' %}
    <span class="status-available">In Stock</span>
  {% when 'backordered' %}
    <span class="status-backordered">Back Ordered</span>
  {% when 'discontinued' %}
    <span class="status-discontinued">No Longer Available</span>
{% endcase %}

Tablerow

/**
 * Special iteration for creating HTML tables
 * Automatically handles table rows and provides tablerowloop object
 */
{% tablerow variable in collection cols: number %}
  <!-- cell content -->
{% endtablerow %}

/**
 * Tablerowloop object properties:
 * - col: current column number (1-based)
 * - col0: current column number (0-based)
 * - col_first: true if first column
 * - col_last: true if last column
 * - first: true if first row
 * - last: true if last row
 * - index: iteration index (1-based)
 * - index0: iteration index (0-based)
 * - length: total iterations
 * - row: current row number (1-based)
 */

Usage Examples:

<table>
  {% tablerow product in products cols: 3 %}
    <td>
      {{ product.name }}
      {% if tablerowloop.col_last %}</tr><tr>{% endif %}
    </td>
  {% endtablerow %}
</table>

<!-- With styling -->
{% tablerow item in items cols: 4 %}
  <td class="{% if tablerowloop.col_first %}first{% endif %}">
    Row {{ tablerowloop.row }}, Col {{ tablerowloop.col }}
  </td>
{% endtablerow %}

Loop Control

/**
 * Break out of current loop
 */
{% break %}

/**
 * Skip to next iteration
 */
{% continue %}

Usage Examples:

{% for product in products %}
  {% if product.hidden %}
    {% continue %}
  {% endif %}
  
  {% if product.featured %}
    <div class="featured">{{ product.name }}</div>
    {% break %}
  {% endif %}
  
  <div>{{ product.name }}</div>
{% endfor %}

Variable Manipulation Tags

Tags for managing variables and their values.

Assignment

/**
 * Assign value to variable
 */
{% assign variable = value %}

/**
 * Increment variable (creates if not exists, starting at 0)
 */
{% increment variable %}

/**
 * Decrement variable (creates if not exists, starting at -1)
 */
{% decrement variable %}

Usage Examples:

{% assign user_name = user.first_name | append: ' ' | append: user.last_name %}
<p>Hello, {{ user_name }}!</p>

{% assign total_price = 0 %}
{% for item in cart.items %}
  {% assign total_price = total_price | plus: item.price %}
{% endfor %}
<p>Total: ${{ total_price }}</p>

<!-- Counter variables -->
{% increment my_counter %}  <!-- Output: 0 -->
{% increment my_counter %}  <!-- Output: 1 -->
{% increment my_counter %}  <!-- Output: 2 -->

{% decrement my_counter %}  <!-- Output: -1 -->
{% decrement my_counter %}  <!-- Output: -2 -->

Capture

/**
 * Capture rendered content into a variable
 */
{% capture variable %}
  <!-- content to capture -->
{% endcapture %}

Usage Examples:

{% capture user_info %}
  <div class="user">
    <h3>{{ user.name }}</h3>
    <p>{{ user.bio | truncatewords: 20 }}</p>
    {% if user.avatar %}
      <img src="{{ user.avatar }}" alt="{{ user.name }}">
    {% endif %}
  </div>
{% endcapture %}

<!-- Use captured content multiple times -->
<div class="sidebar">{{ user_info }}</div>
<div class="main">{{ user_info }}</div>

<!-- Capture complex expressions -->
{% capture product_list %}
  {% for product in products %}
    {{ product.name }}{% unless forloop.last %}, {% endunless %}
  {% endfor %}
{% endcapture %}
<p>Products: {{ product_list }}</p>

Cycle

/**
 * Cycle through values on each call
 * Useful for alternating classes or values
 */
{% cycle 'value1', 'value2', 'value3' %}

/**
 * Named cycles for multiple independent cycles
 */
{% cycle 'group1': 'odd', 'even' %}

Usage Examples:

<!-- Alternating row classes -->
{% for item in items %}
  <tr class="{% cycle 'odd', 'even' %}">
    <td>{{ item.name }}</td>
  </tr>
{% endfor %}

<!-- Multiple independent cycles -->
{% for product in products %}
  <div class="{% cycle 'colors': 'red', 'blue', 'green' %} {% cycle 'sizes': 'small', 'large' %}">
    {{ product.name }}
  </div>
{% endfor %}

Template Composition Tags

Tags for including and composing templates from multiple sources.

Include

/**
 * Include partial template
 * Variables are shared with parent template
 */
{% include 'template_name' %}
{% include template_variable %}

/**
 * Include with additional variables
 */
{% include 'template_name', variable: value %}

/**
 * Jekyll-style include (when jekyllInclude option is true)
 */
{% include 'template_name' variable=value %}

Usage Examples:

<!-- Basic include -->
{% include 'header' %}
<main>{{ content }}</main>
{% include 'footer' %}

<!-- Dynamic include -->
{% assign sidebar_template = 'sidebar-' | append: user.role %}
{% include sidebar_template %}

<!-- Include with variables -->
{% include 'product-card', product: featured_product, featured: true %}

<!-- Jekyll-style -->
{% include 'alert.html' type='warning' message='Please update your profile' %}

Render

/**
 * Render template with isolated scope
 * Only explicitly passed variables are available
 */
{% render 'template_name' %}
{% render 'template_name' with object %}
{% render 'template_name' with object as alias %}
{% render 'template_name' for array %}

Usage Examples:

<!-- Basic render -->
{% render 'button' %}

<!-- Render with data -->
{% render 'product-card' with product %}

<!-- Render with alias -->
{% render 'user-info' with current_user as user %}

<!-- Render for each item in array -->
{% render 'list-item' for menu_items %}

<!-- Render with additional variables -->
{% render 'modal', title: 'Confirm Action', type: 'warning' %}

Layout

/**
 * Wrap current template in layout
 * Content is available as {{ content }} in layout
 */
{% layout 'layout_name' %}

Usage Examples:

<!-- In page template -->
{% layout 'base' %}
<h1>{{ page.title }}</h1>
<p>{{ page.content }}</p>

<!-- In base.liquid layout -->
<!DOCTYPE html>
<html>
<head><title>{{ page.title }}</title></head>
<body>
  <header>{% include 'header' %}</header>
  <main>{{ content }}</main>
  <footer>{% include 'footer' %}</footer>
</body>
</html>

Block

/**
 * Define content blocks for layouts
 * Blocks can have default content
 */
{% block block_name %}
  <!-- default content -->
{% endblock %}

Usage Examples:

<!-- In layout template -->
<head>
  <title>{% block title %}Default Title{% endblock %}</title>
  {% block head %}{% endblock %}
</head>
<body>
  {% block content %}
    <p>Default content</p>
  {% endblock %}
</body>

<!-- In page template using layout -->
{% layout 'base' %}
{% block title %}Custom Page Title{% endblock %}
{% block head %}<link rel="stylesheet" href="custom.css">{% endblock %}
{% block content %}
  <h1>Custom page content</h1>
{% endblock %}

Content Processing Tags

Tags for processing and manipulating content.

Raw

/**
 * Output content without Liquid processing
 * Useful for code examples or conflicting syntax
 */
{% raw %}
  <!-- unprocessed content -->
{% endraw %}

Usage Examples:

{% raw %}
  <script>
    var template = "Hello {{ name }}!";
    // This won't be processed by Liquid
  </script>
{% endraw %}

<!-- Display Liquid syntax in documentation -->
{% raw %}
  Use {% for item in items %} to iterate over arrays.
{% endraw %}

Echo

/**
 * Output expression (alternative to {{ }} syntax)
 */
{% echo expression %}

Usage Examples:

{% echo user.name %}
<!-- Equivalent to {{ user.name }} -->

{% echo products | map: 'name' | join: ', ' %}
<!-- Useful when {{ }} conflicts with other syntax -->

Comment

/**
 * Multi-line comments (not rendered)
 */
{% comment %}
  <!-- comment content -->
{% endcomment %}

/**
 * Inline comments (single line)
 */
{% # This is an inline comment %}

Usage Examples:

{% comment %}
  This template handles user profiles
  Variables available: user, preferences, settings
{% endcomment %}

{% for product in products %}
  {% # Skip discontinued products %}
  {% unless product.discontinued %}
    {{ product.name }}
  {% endunless %}
{% endfor %}

Liquid

/**
 * Execute multiple Liquid statements without output
 * Useful for complex logic blocks
 */
{% liquid
  statement1
  statement2
  statement3
%}

Usage Examples:

{% liquid
  assign total = 0
  for item in cart.items
    assign total = total | plus: item.price
  endfor
  
  if total > 100
    assign discount = 0.1
  else
    assign discount = 0
  endif
  
  assign final_total = total | times: discount | minus: total | abs
%}

<p>Subtotal: ${{ total }}</p>
<p>Discount: ${{ final_total }}</p>
<p>Total: ${{ total | minus: final_total }}</p>

Tag Syntax Rules

General Syntax

<!-- Basic tag -->
{% tag_name %}

<!-- Tag with arguments -->
{% tag_name argument1 argument2 %}

<!-- Tag with named parameters -->
{% tag_name parameter1: value1, parameter2: value2 %}

<!-- Block tags -->
{% tag_name %}
  content
{% endtag_name %}

Variable Scoping

<!-- Variables assigned in tags are available in parent scope -->
{% assign global_var = 'available everywhere' %}

<!-- Loop variables are scoped to the loop -->
{% for item in items %}
  {{ item }} <!-- 'item' only available here -->
{% endfor %}

<!-- Capture creates variables in parent scope -->
{% capture my_content %}Content{% endcapture %}
{{ my_content }} <!-- Available here -->

Whitespace Control

<!-- Normal whitespace -->
{% if condition %}
  content
{% endif %}

<!-- Trim left whitespace -->
{%- if condition -%}
  content
{%- endif -%}

<!-- Trim right whitespace -->
{% if condition -%}
  content
{% endif -%}

Nested Tags

<!-- Tags can be nested -->
{% if user %}
  {% for post in user.posts %}
    {% if post.published %}
      {% capture post_info %}
        {{ post.title | upcase }}
      {% endcapture %}
      <h3>{{ post_info }}</h3>
    {% endif %}
  {% endfor %}
{% endif %}

Error Handling

Most tags respect the global strictVariables and strictFilters options:

// Strict mode - throws on undefined
const engine = new Liquid({ strictVariables: true });

// Lenient mode - continues with undefined
const engine = new Liquid({ strictVariables: false });

docs

analysis.md

built-in-tags.md

configuration.md

context-and-scoping.md

core-engine.md

extensions.md

filesystem.md

filters.md

index.md

tile.json