Shopify section groups are JSON files in a theme’s sections folder, usually header-group.json and footer-group.json, that hold an ordered list of sections for an area the layout controls. You render one in layout/theme.liquid with {% sections 'header-group' %}, and merchants can then add, remove and reorder sections in the header or footer from the theme editor.
That replaces the older pattern of {% section 'announcement-bar' %} and {% section 'header' %} hard-coded into the layout, where the merchant got exactly the sections the developer typed and nothing else. Shopify’s own sections guide is blunt about it: statically rendered sections “can’t be removed or reordered by merchants”, and you should avoid them wherever possible.
Everything below was checked against the section groups reference and the current Dawn source as of October 2026.
What goes in a section group file?
Four keys at the root, all required. type is one of header, footer, aside, or a custom type written as custom.<name>. name is what the editor shows, 50 characters at most. sections maps section IDs to their data, and order lists those IDs in render order. Both of the last two may be empty.
This is close to Dawn’s sections/header-group.json, trimmed to the parts that matter:
{
"type": "header",
"name": "Header group",
"sections": {
"announcement-bar": {
"type": "announcement-bar",
"settings": {
"color_scheme": "scheme-1",
"auto_rotate": false
},
"blocks": {
"announcement-bar-0": {
"type": "announcement",
"settings": {
"text": "Welcome to our store",
"link": ""
}
}
},
"block_order": ["announcement-bar-0"]
},
"header": {
"type": "header",
"settings": {
"menu": "main-menu",
"sticky_header_type": "on-scroll-up"
}
}
},
"order": ["announcement-bar", "header"]
}
The section data uses the same shape as a JSON template and as config/settings_data.json: a type that names the .liquid file without its extension, then settings, blocks and block_order. There is also an optional "disabled": true, which stops the section rendering but leaves it editable in the theme editor.
A few rules turn into errors when you break them. Every non-app section in the group has to exist in the theme, or the group fails. IDs must be unique within the group, and you can’t share one section’s data between two groups. And the cap is the same as for JSON templates: 25 sections per group, 50 blocks per section.
How do you render a section group in the layout?
Use the sections tag (plural) with the file name minus .json:
<body>
{% sections 'header-group' %}
<main id="MainContent" role="main" tabindex="-1">
{{ content_for_layout }}
</main>
{% sections 'footer-group' %}
</body>
The tag belongs in layout files. Shopify outputs the group’s sections in order, each in its usual shopify-section wrapper, with no extra markup between them. Dawn’s stylesheet targets the wrapper class shopify-section-group-header-group, which is handy when you need to layer a sticky header above an announcement bar without touching each section’s markup.
Shopify’s documentation says to avoid mixing groups and static sections in one layout file. If you have to, put “static” in the static section’s name so a merchant can tell which header items they can’t move.
Which sections can a merchant add to the header group?
Without restrictions in its schema, a section with a preset can be added to any template or group. That is rarely what you want. A full-width image banner in the header group is a support ticket waiting to happen.
The section schema has two attributes for this, enabled_on and disabled_on. They replace the older templates attribute, you can use only one of them per section, and each needs at least one of templates or groups. Group values are header, footer, aside, custom.<NAME>, or ["*"].
Dawn’s announcement bar locks itself to the header. Trimmed, its schema looks like this:
{% schema %}
{
"name": "Announcement bar",
"class": "announcement-bar-section",
"enabled_on": {
"groups": ["header"]
},
"settings": [],
"presets": [
{
"name": "Announcement bar"
}
]
}
{% endschema %}
Leaving templates out of enabled_on means it can’t be dropped into a product page either. For content sections that should never appear in the header or footer, flip it round:
"disabled_on": {
"groups": ["header", "footer"]
}
Presets matter as much as these filters. Dawn’s header.liquid and footer.liquid have no presets at all, so a merchant can’t add a second header, and because the group file includes them manually they can’t delete the only one either. That is the right default for anything the theme’s JavaScript assumes exists exactly once. If you need a ceiling rather than an outright ban, the schema’s limit attribute caps how many times a section can be added, to 1 or 2.
How do you migrate static header sections to a section group?
Shopify’s migration guide is three moves. Create sections/header-group.json (and a footer one) that lists every static section it replaces. Swap the static tags in theme.liquid for one {% sections 'header-group' %}. Ship the theme update.
On update, Shopify “attempts to copy” the merchant’s existing static section settings from settings_data.json into the matching section in the new group, and it matches by section type. Two things follow from that.
Keep the section types the same
If your old layout rendered {% section 'header' %} and the group refers to "type": "header", the merchant’s logo position, menu choice and colour scheme carry over. Rename the file to site-header.liquid in the same release and there’s nothing to map to, so the merchant gets your defaults back. Do renames in a separate release, after the migration has run.
Treat “attempts” literally
The guide doesn’t promise the copy succeeds, and it doesn’t say what happens to the old static entries in settings_data.json. Before shipping, install the current version on a development store, configure the header in ways the defaults don’t match (a different menu, a non-default logo position, announcement text), then apply the update and compare. That ten-minute check is cheaper than a merchant noticing their navigation reset.
Keep every section file the group names in the same release. A group that references a missing non-app section is an error, and a release that renames files is exactly where that slips through, so this is a good moment to have Theme Check running in CI.
Can the header differ by market or for B2B buyers?
Yes, and section groups are how it happens. When a merchant adapts a group for a buyer context in the editor, Shopify writes a sibling file named header-group.context.<context-string>.json. It holds only the overrides, a parent pointing at the base file, and a context of either a market handle or "b2b": true:
{
"context": {
"market": "ca"
},
"parent": "header-group.json",
"sections": {
"announcement-bar": {
"blocks": {
"announcement-bar-one": {
"settings": {
"text": "Free shipping for Canada!"
}
}
}
}
}
}
The override is keyed by section ID and block ID. Rename announcement-bar-one in the base file and the Canadian override is pointing at nothing. Merchants create these files in the admin, not in your repo, so if you deploy themes from Git, pull the live theme first and commit them, or a full push can wipe out the merchant’s market changes.
Telling a section where it was rendered
The Liquid section object has a location property. In a group it returns the group type (header, footer, custom.<type>), in a template it returns template, and a statically rendered section gets static. That lets one section file behave sensibly in two places:
{%- liquid
assign in_header = false
if section.location == 'header'
assign in_header = true
endif
-%}
<div class="promo{% if in_header %} promo--compact{% endif %}">
{{ section.settings.text }}
</div>
section.index counts from 1 within each location, so the first section in the header group and the first in the template are both index 1. It returns nil for static sections, inside the theme editor and through the Section Rendering API, so keep it to performance tweaks like lazy-loading and never use it to decide what a merchant sees.
What we would do
Any theme still rendering its header or footer with {% section %} in the layout should move to header and footer groups in its next release. Merchants can then add an announcement bar or reorder the header without a developer, and a market-specific header becomes an editor change instead of a Liquid branch on localization.market. Lock header-only sections with enabled_on, keep presets off anything that must exist exactly once, and test the settings migration on a configured store before you ship.
Hold off on adding custom groups, such as a custom.sidebar, until a merchant actually asks for an editable area there. Shopify’s own advice is that most themes need only the header and footer, and each extra group is one more place for an override file to go stale. Theme blocks are often the better tool for that job, as our comparison of theme blocks and section blocks covers.
We build and maintain Online Store 2.0 themes for UK merchants, including moving older themes onto section groups without wiping the merchant’s settings. Our Shopify development work covers theme builds, Hydrogen storefronts and migrations onto Shopify.