Skip to content

Section schema reference

Each section file (sections/*.liquid) ends with one {% schema %} block of JSON that tells the visual editor what the section is and how merchants can configure it. The schema is validated when a theme is imported, saved, or rendered — an invalid schema blocks the save and shows a specific error, so the rules below are enforced, not advisory.

Top-level keys

These are the only keys a section schema may contain. Any other key fails validation with contains unsupported schema attribute.

KeyTypePurpose
namestringSection name shown in the editor
tagstringWrapper HTML element — one of article, aside, div, footer, header, section (default div)
classstringCSS class(es) added to the wrapper
settingsarrayThe section’s settings
blocksarrayBlock types the section accepts (local or theme — see below)
max_blocksnumberMaximum total blocks of any type — an integer from 0 to 50
limitnumberHow many instances of this section a template may contain — must be 1 or 2
presetsarrayMakes the section addable in the editor, with default settings and blocks
defaultobjectDefault settings/blocks baked into JSON templates when the section is placed
enabled_on / disabled_onobjectRestrict which templates and section groups can use it (use one, not both)
localesobjectSection-scoped translations

Blocks: two mutually exclusive kinds

A section accepts blocks through its blocks array. There are two kinds, and a single section may use one kind or the other — never both. Mixing them fails with a section cannot mix section-defined blocks with theme blocks.

A block entry is treated as a local (section-defined) block if it has a name string or a settings array. Otherwise it’s a theme block reference — just a type (optionally with limit).

Local blocks

Local blocks are defined inline in the section and rendered by the section itself. Use them when the block only makes sense inside this one section.

{
"type": "testimonial",
"name": "Testimonial",
"limit": 6,
"settings": [
{ "type": "richtext", "id": "quote", "label": "Quote" },
{ "type": "text", "id": "author", "label": "Author" }
]
}

In the section body, render local blocks by iterating section.blocks:

{% for block in section.blocks %}
{% case block.type %}
{% when 'testimonial' %}
<blockquote {{ block.shopify_attributes }}>{{ block.settings.quote }}</blockquote>
{% endcase %}
{% endfor %}

Each local block type must be unique within the section, and each local block name must be unique too.

Theme blocks

Theme blocks are reusable files in your theme’s blocks/ folder — each blocks/*.liquid file ends in its own {% schema %}. A section opts into them by listing the block type (the filename without extension) with no name and no settings:

"blocks": [
{ "type": "@theme" },
{ "type": "_divider" }
]
  • { "type": "@theme" } accepts any public theme block in blocks/.
  • { "type": "slide" } accepts only blocks/slide.liquid.
  • A block whose filename starts with _ (e.g. blocks/_divider.liquid) is private: it is not matched by the @theme wildcard and must be listed by its exact type to be allowed.

A theme block’s own schema supports a smaller key set than a section — only name, settings, blocks, presets, tag, and class. Theme blocks can nest other theme blocks via their own blocks array, which is how you build composable layouts. See Theme blocks for the full block-file authoring guide.

Rendering theme blocks with content_for

Theme blocks are not rendered with a {% for %} loop. Instead, the section emits the runtime placeholder that the editor fills with the merchant’s chosen, reordered blocks:

{% content_for 'blocks' %}

This single tag replaces the {% for block in section.blocks %} pattern. It has strict placement rules, enforced at render time:

  • It cannot appear inside an {% if %}, {% unless %}, {% case %}, or {% for %} — doing so fails with cannot be rendered conditionally or in a for-loop.
  • It only renders theme blocks. If the section’s blocks array defines local blocks, content_for 'blocks' errors — iterate section.blocks instead.
  • It is only valid inside section files, theme block files, or snippets rendered from one of those.

To render a single fixed theme block at a known position, use the static form:

{% content_for 'block', type: 'announcement', id: 'top-bar' %}

Static blocks

Static blocks render at a fixed position you choose in the section, rather than in the merchant-reorderable list. You declare them in a preset’s blocks array with "static": true and a stable id:

"blocks": [
{ "type": "announcement", "static": true, "id": "top-bar" },
{ "type": "@theme" }
]

Rules:

  • A static block must have an id containing only letters, numbers, _, or -.
  • Two static blocks with the same id in the same immediate parent are not allowed (duplicate static block id).
  • The referenced theme block must be targeted by the section’s blocks (its exact type, or @theme for non-private blocks).

Nesting and block limits

  • max_blocks caps the total number of blocks a merchant can add to the section. It must be an integer from 0 to 50. The platform also enforces a hard ceiling of 50 blocks per section regardless of max_blocks.
  • Nesting depth — theme blocks can contain theme blocks, but the nesting is capped at a maximum depth of 8. Presets that nest deeper fail validation.
  • A per-type limit on a block entry caps how many of that one type can be added (also 050).

Presets

Without a preset, a section can only be referenced from theme code. With one, merchants can add it from the editor’s section picker. A preset can set default settings, seed default (and static) blocks, and group the section under a category in the picker:

"presets": [
{
"name": "Testimonials",
"category": "Social proof",
"settings": { "heading": "What customers say" },
"blocks": [
{ "type": "testimonial" },
{ "type": "testimonial" }
]
}
]
  • name is required.
  • Keys in settings must match the ids you declared in the section’s settings — an unknown key fails with contains unknown setting.
  • Block entries in a preset must be allowed by the section’s blocks array.

The default key works the same way but applies when the section is placed into a JSON template directly, rather than added from the editor.

Placement rules

enabled_on allowlists where a section may be used; disabled_on blocklists instead. Use one or the other — a schema with both fails validation.

"enabled_on": { "templates": ["index", "page"], "groups": ["footer"] }
  • templates are template names such as index, product, collection, page, cart, metaobject, policy, or * for all. Customer account documents are system-owned and are not valid section-placement targets.
  • groups are section-group tokens: *, header, footer, aside, or a custom.<name> group.

This is how header/footer-only sections are kept out of the main page area, and vice versa. See Section groups for how groups map to layout regions.

The shopify_attributes requirement

{{ block.shopify_attributes }} is what lets the visual editor highlight, select, and reorder blocks on the live preview. Always output it on each block’s wrapper element.

Complete example

{% schema %}
{
"name": "Featured collection",
"tag": "section",
"class": "featured-collection",
"limit": 2,
"settings": [
{ "type": "text", "id": "heading", "label": "Heading", "default": "Featured" },
{ "type": "collection", "id": "collection", "label": "Collection" },
{ "type": "range", "id": "products_to_show", "label": "Products", "min": 2, "max": 12, "step": 1, "default": 4 },
{ "type": "checkbox", "id": "show_prices", "label": "Show prices", "default": true }
],
"blocks": [
{ "type": "promo_card", "name": "Promo card", "settings": [
{ "type": "image_picker", "id": "image", "label": "Image" },
{ "type": "url", "id": "link", "label": "Link" }
]}
],
"max_blocks": 4,
"presets": [{ "name": "Featured collection" }]
}
{% endschema %}

This example uses a local block (promo_card has a name and settings), so it would render with {% for block in section.blocks %}. To switch to theme blocks instead, replace the blocks array with [{ "type": "@theme" }], render with {% content_for 'blocks' %}, and remove the local block’s name/settings.

Troubleshooting validation errors

a section cannot mix section-defined blocks with theme blocks — your blocks array has at least one entry with a name/settings (local) and at least one bare-type entry (theme). Pick one kind for the whole section.

Next steps