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.
| Key | Type | Purpose |
|---|---|---|
name | string | Section name shown in the editor |
tag | string | Wrapper HTML element — one of article, aside, div, footer, header, section (default div) |
class | string | CSS class(es) added to the wrapper |
settings | array | The section’s settings |
blocks | array | Block types the section accepts (local or theme — see below) |
max_blocks | number | Maximum total blocks of any type — an integer from 0 to 50 |
limit | number | How many instances of this section a template may contain — must be 1 or 2 |
presets | array | Makes the section addable in the editor, with default settings and blocks |
default | object | Default settings/blocks baked into JSON templates when the section is placed |
enabled_on / disabled_on | object | Restrict which templates and section groups can use it (use one, not both) |
locales | object | Section-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 inblocks/.{ "type": "slide" }accepts onlyblocks/slide.liquid.- A block whose filename starts with
_(e.g.blocks/_divider.liquid) is private: it is not matched by the@themewildcard and must be listed by its exacttypeto 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 withcannot be rendered conditionally or in a for-loop. - It only renders theme blocks. If the section’s
blocksarray defines local blocks,content_for 'blocks'errors — iteratesection.blocksinstead. - 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
idcontaining only letters, numbers,_, or-. - Two static blocks with the same
idin the same immediate parent are not allowed (duplicate static block id). - The referenced theme block must be targeted by the section’s
blocks(its exacttype, or@themefor non-private blocks).
Nesting and block limits
max_blockscaps the total number of blocks a merchant can add to the section. It must be an integer from0to50. The platform also enforces a hard ceiling of 50 blocks per section regardless ofmax_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
limiton a block entry caps how many of that one type can be added (also0–50).
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" } ] }]nameis required.- Keys in
settingsmust match theids you declared in the section’ssettings— an unknown key fails withcontains unknown setting. - Block entries in a preset must be allowed by the section’s
blocksarray.
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"] }templatesare template names such asindex,product,collection,page,cart,metaobject,policy, or*for all. Customer account documents are system-owned and are not valid section-placement targets.groupsare section-group tokens:*,header,footer,aside, or acustom.<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.
section "limit" ... must be 1 or 2 — limit is restricted to 1 or 2.
Invalid section tag — tag must be one of article, aside, div,
footer, header, section.
max_blocks ... must be an integer between 0 and 50 — the value is out of range,
a fraction, or not a number.
duplicate static block id — two static blocks share an id under the same
immediate parent; give them distinct ids.
must include {{ block.shopify_attributes }} — a tag: null theme block is
missing the attribute on its top-level element.
can use only one of "enabled_on" or "disabled_on" — remove whichever one you
don’t need.
Next steps
- Theme blocks — author reusable
blocks/*.liquidfiles. - Setting types — every input you can put in
settings. - Section groups — how
groupsplacement maps to layout regions. - Theme architecture — where sections fit in the theme.