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.
{% 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 %}| Property | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Identifier for the block type, referenced as block.type in Liquid. |
label | string | Yes | The block name shown in the theme editor. Can be a t: locale key. |
limit | integer | No | Maximum number of blocks of this type in the section. Defaults to 50, and can't exceed 50. |
settings | array | No | Setting 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.
<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:
{% 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.
{% 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.
{%- 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:
{% 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
| Property | Description |
|---|---|
block.id | Unique identifier for the block instance. Stable across edits; useful for scoping CSS or id attributes. |
block.type | The block's type as declared in the section schema. |
block.settings | The block's setting values, keyed by setting id. |
block.youcan_attributes | Theme-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
- In a section added through a JSON template, each block is an entry in that section's
blocksobject, and the section'sorderarray sets the sequence. - In a statically rendered section, blocks come from the matching section entry in
config/settings_data.json.
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.
{% 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:
{% for item in section.blocks %}
<li {{ item.youcan_attributes }}>{{ item.settings.text }}</li>
{% endfor %}