QUANTM7 Docs

Template Tags

Including partials with render, defining section schemas, and scoped styles.

Template tags control the structure of your templates. They let you include reusable fragments, define section settings, and scope CSS and JavaScript to a single section.

layout

By default, every template uses the theme.liquid layout. To use a different layout, add a layout tag at the top of the template file:

{% layout 'checkout' %}

This tells Quill to wrap the template in checkout.liquid instead of the default layout. The named layout file must exist in the layout/ folder.

To render a template with no layout at all, use none:

{% layout none %}

This is useful for pages that need complete control over the HTML, such as landing pages or custom iframes.

render

Include a reusable snippet (partial template) inside your template:

{% render 'product-card' %}

This inserts the content of the product-card snippet at that position.

Passing data to snippets

By default, snippets have isolated scope. They cannot access variables from the parent template. Pass data explicitly:

{% render 'product-card' with product %}

The with keyword passes a single object. Inside the snippet, it is available as a variable with the same name as the snippet. In this case, the snippet receives a product-card variable, but you can rename it:

{% render 'product-card' with featured_product as product %}

Now the snippet receives the value as product.

Passing named variables

Pass one or more named variables:

{% render 'product-card', product: product, show_price: true, layout: 'grid' %}

Inside the snippet, product, show_price, and layout are all available.

Looping with render

Render a snippet once for each item in an array:

{% render 'product-card' for collection.products as product %}

This is the same as writing a for loop around a render call. The snippet receives each item as product. Inside the snippet, forloop variables are also available.

section

Load a named section into the template:

{% section 'featured-collection' %}

Sections are self-contained blocks with their own template, schema, CSS, and JavaScript. They are the building blocks of a theme. The visual editor lets merchants add, remove, and reorder sections on a page.

schema

Define the settings for a section. The visual editor reads this schema and builds a form in the sidebar:

{% schema %}
{
  "name": "Hero Banner",
  "settings": [
    {
      "type": "text",
      "id": "heading",
      "label": "Heading",
      "default": "Welcome"
    },
    {
      "type": "image_picker",
      "id": "background",
      "label": "Background image"
    },
    {
      "type": "select",
      "id": "height",
      "label": "Section height",
      "options": [
        { "value": "small", "label": "Small" },
        { "value": "medium", "label": "Medium" },
        { "value": "large", "label": "Large" }
      ],
      "default": "medium"
    }
  ]
}
{% endschema %}

Access these settings in your template with section.settings:

<div class="hero hero--{{ section.settings.height }}">
  <h1>{{ section.settings.heading }}</h1>
</div>

The merchant changes these values through the visual editor. No code changes are needed. See the Section Schema reference for all setting types.

include (deprecated)

The include tag was the old way to insert a snippet. It still works, but you should use render instead:

{% comment %} Old way (deprecated) {% endcomment %}
{% include 'product-card' %}

{% comment %} New way (use this) {% endcomment %}
{% render 'product-card' %}

The key difference is scope. With include, the snippet can access all variables from the parent template. With render, the snippet has isolated scope and only sees data you pass to it explicitly. Isolated scope is safer and easier to reason about.

stylesheet

Add CSS that is scoped to the current section:

{% stylesheet %}
  .hero {
    padding: 4rem 2rem;
    text-align: center;
  }
  .hero--small { min-height: 300px; }
  .hero--medium { min-height: 500px; }
  .hero--large { min-height: 700px; }
{% endstylesheet %}

The CSS is automatically scoped so it only affects elements inside this section. It will not leak into other parts of the page.

javascript

Add JavaScript that is scoped to the current section:

{% javascript %}
  const section = document.querySelector('[data-section-id="{{ section.id }}"]');
  // Your section-specific JS here
{% endjavascript %}

Section JavaScript runs only on the published storefront, not in the editor preview. It is wrapped in an isolated scope so it cannot conflict with other sections.

style

Inline a <style> block that can use Liquid variables. Unlike stylesheet, which is scoped and static, style lets you inject dynamic values from section settings:

{% style %}
  .hero-{{ section.id }} {
    background-color: {{ section.settings.bg_colour }};
    color: {{ section.settings.text_colour }};
    padding: {{ section.settings.padding }}px 0;
  }
{% endstyle %}

Use style when you need CSS values that come from merchant settings. Use stylesheet for static CSS that does not change per section instance.

form

Generate an HTML form with the correct action URL and hidden fields:

{% form 'product', product %}
  <select name="variant_id">
    {% for variant in product.variants %}
      <option value="{{ variant.id }}">
        {{ variant.title }} - {{ variant.price | money }}
      </option>
    {% endfor %}
  </select>
  <button type="submit">Add to Cart</button>
{% endform %}

The form tag handles the form's action, method, and any required hidden fields. You only need to write the visible form elements.

Supported form types:

TypePurpose
'product'Add a product to the cart
'cart'Update the cart
'customer_login'Log in
'create_customer'Register a new account
'customer_address'Add or edit an address
'contact'Contact form

paginate

Wrap a collection to enable pagination:

{% paginate collection.products by 24 %}
  {% for product in collection.products %}
    <div class="product-card">
      <h3>{{ product.title }}</h3>
    </div>
  {% endfor %}

  {{ paginate | default_pagination }}
{% endpaginate %}

The by parameter sets how many items per page. The default_pagination filter renders page navigation links (Previous, 1, 2, 3, Next).

Inside a paginate block, the paginate object gives you:

PropertyDescription
paginate.pagesTotal number of pages
paginate.current_pageCurrent page number
paginate.itemsTotal number of items
paginate.page_sizeItems per page
paginate.previousLink to the previous page (nil if on first page)
paginate.nextLink to the next page (nil if on last page)
paginate.partsArray of page links for building custom pagination

On this page