Skip to content

Blocks ​

Blocks are repeatable, individually configurable items inside a section. Each section declares which block types it accepts; sellers then add, remove, reorder, and configure block from the theme editor.

Common uses are slides in a slideshow, columns in a multi-column layout, items in a feature list, and products in a featured collection.

Location ​

Blocks aren't separate files. A block type is defined in the blocks array of a section's {% schema %}, and rendered by that section's Liquid. Two sections can define unrelated block types that happen to share a name.

Defining blocks ​

Each entry in the section schema's blocks array describes one block type.

liquid
{% schema %}
{
  "label": "Multi-column",
  "max_blocks": 6,
  "settings": [],
  "blocks": [
    {
      "type": "column",
      "label": "Column",
      "limit": 6,
      "settings": [
        { "type": "text", "id": "heading", "label": "Heading" },
        { "type": "textarea", "id": "body", "label": "Text" }
      ]
    }
  ]
}
{% endschema %}
PropertyTypeRequiredDescription
typestringYesIdentifier for the block type, referenced as block.type in Liquid.
labelstringYesThe block name shown in the theme editor. Can be a t: locale key.
limitintegerNoMaximum number of blocks of this type in the section. Defaults to 50, and can't exceed 50.
settingsarrayNoSetting definitions for the block, read through block.settings.

max_blocks on the section schema caps the total number of blocks across all types (default and maximum 25).

Rendering blocks ​

Iterate section.blocks in the order the seller arranged them, and branch on block.type.

liquid
<div class="multi-column">
  {% for block in section.blocks %}
    {% case block.type %}
      {% when 'column' %}
        <div class="column" {{ block.youcan_attributes }}>
          <h3>{{ block.settings.heading }}</h3>
          <p>{{ block.settings.body }}</p>
        </div>
    {% endcase %}
  {% endfor %}
</div>

Inside the loop you also have the standard Liquid forloop object, so you can vary markup by position:

liquid
{% for block in section.blocks %}
  <div class="slide" data-index="{{ forloop.index }}" {{ block.youcan_attributes }}>
    ...
  </div>
{% endfor %}

Blocks as snippets ​

When a block's markup grows, move it into its own snippet and keep one snippet per block type. A common convention is to name the file block.<type>.liquid.

liquid
{% for block in section.blocks %}
  {% case block.type %}
    {% when 'slide' %}
      {% render 'block.slide' %}
    {% when 'testimonial' %}
      {% render 'block.testimonial' %}
  {% endcase %}
{% endfor %}

The loop variable is available inside the snippet under the same name, so {% for slide in section.blocks %} makes slide.settings and slide.youcan_attributes usable in block.slide.liquid. The section object is in scope there too.

liquid
{%- comment -%} snippets/block.slide.liquid {%- endcomment -%}
<li ui-block="slide" {{ slide.youcan_attributes }}>
  <h2>{{ slide.settings.heading }}</h2>
  <p>{{ slide.settings.text }}</p>
</li>

Empty state ​

A section can be placed with no blocks yet, or with blocks that aren't filled in. Guard for that so the editor still renders something to click:

liquid
{% if section.blocks.size == 0 %}
  <p class="placeholder">Add a column to get started.</p>
{% else %}
  {% for block in section.blocks %}
    ...
  {% endfor %}
{% endif %}

The block object ​

PropertyDescription
block.idUnique identifier for the block instance. Stable across edits; useful for scoping CSS or id attributes.
block.typeThe block's type as declared in the section schema.
block.settingsThe block's setting values, keyed by setting id.
block.youcan_attributesTheme-editor attributes to spread onto the block's root element. See below.

section.blocks.size gives the number of blocks.

Where block data comes from ​

Either way, section.blocks in Liquid reflects the resulting list, already ordered.

Integrate blocks with the theme editor ​

For the theme editor to select, highlight, and live-update an individual block, output block.youcan_attributes on that block's outermost rendered element.

liquid
{% for block in section.blocks %}
  {% case block.type %}
    {% when 'heading' %}
      <h2 {{ block.youcan_attributes }}>{{ block.settings.content }}</h2>
    {% when 'button' %}
      <a href="{{ block.settings.link }}" {{ block.youcan_attributes }}>
        {{ block.settings.label }}
      </a>
  {% endcase %}
{% endfor %}

This outputs a data-youcan-editor-block attribute the editor uses to map the DOM node back to the block. Without it, the block can't be clicked in the editor, and changing its settings forces a full section reload instead of updating in place.

When you iterate blocks under a custom name, reference that name instead:

liquid
{% for item in section.blocks %}
  <li {{ item.youcan_attributes }}>{{ item.settings.text }}</li>
{% endfor %}