Sections & Blocks
What makes a section different from a plain Liquid file is a {% schema %} tag at the bottom, declaring what settings and blocks the section exposes to the Theme Editor. That one tag is the entire bridge between a developer's code and a merchant's drag-and-drop customization.
Anatomy of a section
<div class="announcement-bar">
<p>{{ section.settings.message }}</p>
</div>
{% schema %}
{
"name": "Announcement bar",
"settings": [
{
"type": "text",
"id": "message",
"label": "Message",
"default": "Free shipping over $50"
}
]
}
{% endschema %}
Whatever a merchant types into the "Message" field in the Theme Editor becomes available in the template as section.settings.message — the schema's id is the key that connects the two.
Blocks: repeatable sub-items
Some sections need a variable number of repeatable pieces — testimonials, FAQ entries, logo lists. That's what blocks are for: a merchant adds, removes, and reorders blocks in the Theme Editor, and the section loops over whatever blocks currently exist:
<div class="testimonials">
{% for block in section.blocks %}
<blockquote {{ block.shopify_attributes }}>
{{ block.settings.quote }}
</blockquote>
{% endfor %}
</div>
{% schema %}
{
"name": "Testimonials",
"blocks": [
{
"type": "testimonial",
"name": "Testimonial",
"settings": [
{ "type": "textarea", "id": "quote", "label": "Quote" }
]
}
]
}
{% endschema %}
{{ block.shopify_attributes }} injects hidden data attributes the Theme Editor needs to highlight and select that specific block on the page — it's easy to forget and harmless to include even where it's not strictly needed.
Reserving space for apps: Theme App Extensions
A block's type can also be "@app", which reserves a slot in a section that an installed app can drop its own block into. This is one half of what Shopify calls a Theme App Extension — a bundle of Liquid, CSS, and JavaScript that lives entirely inside an app's own codebase (not the theme's) and gets injected into whichever slots a merchant chooses, without the theme developer ever having to write app-specific code:
{
"blocks": [
{ "type": "@app" },
{ "type": "text", "name": "Text" }
]
}
The other half is an app embed block — a similar app-owned piece of Liquid, but meant for something that doesn't need a specific position in a section, like a chat widget or an analytics snippet a merchant simply toggles on for the whole store from the Theme Editor's "App embeds" panel.
Static vs. dynamic sections
By default, a section added to a JSON template is "dynamic" — a merchant can add, remove, and reorder it freely. A section can instead be marked "enabled_on" with specific rules, or included directly in theme.liquid as a static section (using {% section 'header' %}) when it should always exist in a fixed position, like a header or footer that shouldn't be removable from the page.
Section groups
Headers and footers are handled slightly differently — they live in section groups (JSON files in sections/ ending in .json, like header-group.json) that are shared across every template automatically, rather than being placed page by page.
type field also supports images, colors, checkboxes, product pickers, and more, all rendered as the appropriate control in the Theme Editor automatically.