Skip to main content
Back to Blog
Shopify Section Schema: The Complete Reference (2026)
Reference

Shopify Section Schema: The Complete Reference (2026)

Every section schema attribute explained — name, tag, class, limit, settings, blocks, max_blocks, presets, default, locales, enabled_on and disabled_on — with annotated examples and the rules you can't break.

12 min read

The {% schema %} tag is the contract between your Liquid markup and the Shopify theme editor. It tells Shopify what a merchant can configure, how blocks behave, and where a section is allowed to appear. Get it right and your section is flexible and editor-friendly; get it wrong and you'll see syntax errors or settings that silently do nothing.

This is a complete reference to every section schema attribute, the rules that govern the tag, and the patterns that separate a beginner section from a production one. To generate a valid schema for any layout in seconds, use the Schema Generator; to look up the Liquid that reads these settings, keep the cheatsheet handy.

The rules of {% schema %}

Before the attributes, internalize these constraints — most "my section won't save" problems trace back to one of them:

  • A section file may contain exactly one {% schema %} tag.
  • The tag must contain valid JSON — no comments, no trailing commas, no Liquid.
  • It cannot be nested inside another Liquid tag.
  • It doesn't output anything or run any Liquid inside it.
  • It can be placed anywhere in the file, but convention is the bottom.

Section attributes at a glance

A section schema supports the following top-level attributes:

Attribute Purpose
name Title shown in the theme editor
tag HTML element that wraps the section output
class Extra CSS class on the wrapper
limit Max times the section can be added to a template
settings Array of merchant-configurable inputs
blocks Repeatable, reorderable child components
max_blocks Cap on the number of blocks (max 50)
presets Makes the section available as a dynamic section
default Default configuration for statically rendered sections
locales Section-specific translation strings
enabled_on Restrict where the section can appear
disabled_on Prevent the section from appearing in specific places

Let's go through each.

name

The human-readable title in the "Add section" menu and section list.

{ "name": "Hero Banner" }

tag and class

By default Shopify wraps each section in a <div id="shopify-section-{{ section.id }}">. Use tag to change the wrapper element and class to add classes:

{ "name": "Hero Banner", "tag": "section", "class": "section-hero" }

limit

Restricts how many instances of the section a merchant can add to a single template. Useful for sections that should be unique, like a main product section:

{ "name": "Header", "limit": 1 }

settings

The array of inputs that appear in the editor. Each setting needs a type, most need an id and label, and many accept a default. There are basic inputs (text, textarea, richtext, inline_richtext, number, range, checkbox, radio, select, liquid) and specialized ones (image_picker, video, video_url, url, collection, collection_list, product, product_list, article, blog, page, link_list, font_picker, html, color, color_background, color_scheme, color_palette, text_alignment, metaobject, metaobject_list). There are also two sidebar types that carry no value: header and paragraph.

"settings": [
  { "type": "header", "content": "Content" },
  { "type": "text", "id": "heading", "label": "Heading", "default": "Welcome" },
  { "type": "richtext", "id": "body", "label": "Body" },
  {
    "type": "select",
    "id": "layout",
    "label": "Layout",
    "options": [
      { "value": "grid", "label": "Grid" },
      { "value": "list", "label": "List" }
    ],
    "default": "grid"
  },
  {
    "type": "range",
    "id": "padding",
    "label": "Padding",
    "min": 0, "max": 100, "step": 4, "unit": "px",
    "default": 40
  }
]

You access each value in Liquid with section.settings.<id>. For a full breakdown of every type and when to use it, see the Schema Generator.

Conditional settings with visible_if

Most settings support visible_if, which shows or hides a setting based on another value — perfect for progressive disclosure:

{
  "type": "select", "id": "layout_style", "label": "Layout",
  "options": [{ "value": "flex", "label": "Stack" }, { "value": "grid", "label": "Grid" }],
  "default": "flex"
},
{
  "type": "range", "id": "columns", "label": "Columns",
  "min": 2, "max": 6, "step": 1, "default": 3,
  "visible_if": "{{ section.settings.layout_style == 'grid' }}"
}

blocks and max_blocks

Blocks are repeatable components a merchant can add, remove, and reorder. Each block has its own type, name, and settings. max_blocks caps the count (the platform limit is 50):

"blocks": [
  {
    "type": "feature",
    "name": "Feature",
    "settings": [
      { "type": "text", "id": "title", "label": "Title" },
      { "type": "richtext", "id": "text", "label": "Text" }
    ]
  }
],
"max_blocks": 6

In markup you loop section.blocks and must output {{ block.shopify_attributes }} on each block's wrapper so the editor can target it:

{% for block in section.blocks %}
  <div {{ block.shopify_attributes }}>
    {% case block.type %}
      {% when 'feature' %}
        <h3>{{ block.settings.title }}</h3>
        <div>{{ block.settings.text }}</div>
    {% endcase %}
  </div>
{% endfor %}

To accept theme blocks (reusable blocks in the blocks/ folder) or app blocks, use the special types { "type": "@theme" } and { "type": "@app" }, and render them with {% content_for 'blocks' %}.

Dynamic block titles

The theme editor labels a block in the sidebar using the value of a setting whose id is, in order of preference, heading, then title, then text. If none exist, it falls back to the block's name. Naming a text setting heading gives merchants a readable block label for free.

presets

A preset makes your section appear in the theme editor's "Add section" menu (i.e. a dynamic section). Presets can pre-populate settings and blocks:

"presets": [
  {
    "name": "Hero Banner",
    "settings": { "heading": "Summer Sale" },
    "blocks": [{ "type": "feature" }, { "type": "feature" }]
  }
]

A section with presets shouldn't be statically rendered. If you render a section with {% section 'name' %}, configure it with default instead.

default

default has the same shape as a preset but applies to statically rendered sections — those included directly in a template or layout. Use it to ship a sensible starting configuration.

locales

Section-level translations, separate from the theme's locales/ directory. Handy for sections distributed across multiple themes or shops:

"locales": {
  "en": { "title": "Slideshow" },
  "fr": { "title": "Diaporama" }
}

enabled_on and disabled_on

Control where a section can be used by template and section group. Use only one of the two:

"enabled_on": {
  "templates": ["index", "product"],
  "groups": ["header", "footer"]
}
"disabled_on": {
  "templates": ["password"]
}

A complete, annotated example

Putting it together:

{% schema %}
{
  "name": "Hero Banner",
  "tag": "section",
  "class": "section-hero",
  "limit": 2,
  "settings": [
    { "type": "text", "id": "heading", "label": "Heading", "default": "Welcome" },
    { "type": "richtext", "id": "subheading", "label": "Subheading" },
    { "type": "color", "id": "text_color", "label": "Text color", "default": "#ffffff" }
  ],
  "blocks": [
    {
      "type": "feature",
      "name": "Feature",
      "settings": [
        { "type": "text", "id": "title", "label": "Title" }
      ]
    }
  ],
  "max_blocks": 4,
  "enabled_on": { "templates": ["index"] },
  "presets": [
    { "name": "Hero Banner", "blocks": [{ "type": "feature" }] }
  ]
}
{% endschema %}

Common mistakes

  • Trailing commas or comments in the JSON — both cause a save error.
  • More than one {% schema %} tag — only one is allowed per file.
  • Forgetting {{ block.shopify_attributes }} — blocks become un-selectable in the editor.
  • Using both enabled_on and disabled_on — pick one.
  • Adding presets to a statically rendered section — use default instead.
  • Duplicate setting ids — later values silently overwrite earlier ones.

Build it faster

You don't have to hand-write schema JSON. The Schema Generator builds valid settings and blocks for you, and the HTML to Liquid converter produces both the markup and a matching schema from a block of HTML. If you're new to the workflow, start with our guide on converting HTML into a Shopify section.

Conclusion

The section schema is small but precise: a dozen attributes, strict JSON, and a few firm rules. Once you know what each attribute does — and which patterns (presets vs default, enabled_on vs disabled_on, blocks vs settings) apply when — you can author sections that are powerful for developers and effortless for merchants.

Keep learning: explore every input setting type, reference the full Liquid cheatsheet, and try the free converter.

Found this helpful?

Share it with your network!

Ready to Convert HTML to Liquid?

Try our free HTML to Liquid converter and build your Shopify themes faster.

Try HTML2Liquid Now