Knowledge Hub
Sections are the building blocks of OS 2.0 themes
A Shopify section is a self-contained .liquid file with markup, optional scoped CSS, and a JSON schema block. Merchants add, remove, reorder, and configure sections in the theme editor without touching code. JSON templates—index.json, product.json—declare which sections appear on each page type.
JSON templates explained
JSON templates replace monolithic Liquid template files. Each template file lists section instances by key, references the section type (filename), and stores default settings. The order array controls render sequence on the storefront.
Simplified index.json
{
"sections": {
"hero": { "type": "image-banner", "settings": {} },
"featured": { "type": "featured-collection", "settings": { "collection": "frontpage" } }
},
"order": ["hero", "featured"]
}Schema connects sections to the editor
Schema defines what merchants can change. settings arrays hold section-level fields; blocks arrays define repeatable row types. presets expose sections in the Add section panel. Read /shopify-schema-guide for setting types and validation rules.
Blocks and repeatable content
Blocks let merchants scale content—FAQ rows, slides, feature cards. Liquid loops {% for block in section.blocks %} render each row. block.shopify_attributes links editor selection to DOM nodes. /dynamic-shopify-blocks-guide covers block patterns in depth.
Presets and merchant discovery
Without presets, custom sections are invisible in the Add section UI. A preset names the section and can seed default blocks and settings. Ship multiple presets when one section supports distinct layouts—hero vs split hero, FAQ vs compact FAQ.
Section groups
Section groups bundle shared areas—header and footer—across templates. Header groups contain logo, menu, and announcement sections. When adding announcement bars, confirm whether your theme expects them in the header group or as page-level sections.
Dynamic vs static section placement
- Static placement — developer adds section to JSON template in code.
- Dynamic placement — merchant adds section via editor; requires presets.
- App blocks — apps inject into @app block slots inside compatible sections.
From architecture to implementation
Use /shopify-section-tutorial for step-by-step shipping workflow, /converter to generate sections from HTML, and theme guides like /html-to-dawn-theme for deployment on specific themes. /shopify-liquid-examples provides copy-ready Liquid for common components. Guides are maintained by the K2 Devworks team — see /meet-the-founders for author profiles and /shopify-development-experience for delivery background.
Debugging missing or duplicate sections
When a section does not appear on the storefront, verify the chain: the section file exists in sections/, the JSON template references the correct type string matching the filename, and the instance key is listed in order. Shopify does not render sections omitted from order even if they exist in the sections object. Duplicate sections on the homepage usually mean the same preset was added dynamically while a static JSON entry already exists—remove one source of truth. Theme editor preview showing a section but live site hiding it often means different template files between preview context and published theme—confirm you edited the published theme id.
Schema validation errors prevent sections from appearing in Add section. Open the section file, scroll to {% schema %}, and validate JSON in an external linter. Trailing commas after the last setting object break parsing. When migrating, compare old Liquid template section tags to new JSON keys—teams often register the section file but forget to add it to collection.json.
How sections relate to theme.liquid
layout/theme.liquid wraps every page and typically renders header group, main template content, and footer group. The main content area outputs JSON template sections in order—theme.liquid does not list individual marketing modules. Avoid stuffing promotional HTML into layout unless it truly must appear on every route including checkout-adjacent pages. Global scripts, font preloads, and meta tags belong in layout; campaign heroes belong in sections. Merchants cannot reorder layout hardcoding without developer access.
Online Store 2.0 still uses theme.liquid for the HTML shell—do not confuse removing .liquid product templates with removing layout. Sections slot into the main content injection point defined by the parent theme. Custom landing pages using page.json follow the same rules as index.json: an order array of section instance keys. Gift card and password templates also use JSON—verify your parent theme before copying index patterns blindly.
Sections explained without jargon
Think of a Shopify storefront page as a vertical stack of independent modules. Each module is a section: a .liquid file in the theme's sections/ folder plus a JSON schema block at the bottom. Merchants rearrange and configure those modules in the theme editor. Online Store 2.0 replaced old monolithic template files with JSON templates—index.json, product.json, collection.json—that list which section instances appear and in what order. A section does not know which page it is on unless you branch on template.name or request.page_type; it only knows its own section.settings and section.blocks. Snippets are different: reusable partials without schema, loaded with {% render %}. Sections are merchant-facing; snippets are developer-facing building blocks inside sections. That separation is the core OS 2.0 architecture this guide documents.
OS 2.0 page assembly
layout/theme.liquid
│
▼
templates/product.json (order array)
│
├─► sections/main-product.liquid
├─► sections/apps.liquid
└─► sections/related-products.liquid
each file: Liquid + {% schema %}JSON templates declare order; section files declare markup and editor fields.
Real-world workflow: from ticket to live section
- 01
Confirm placement: template vs section group
Homepage modules go in templates/index.json. Announcement bars often belong in the header section group. Putting a promo in the wrong container means merchants cannot find it where they expect.
- 02
Build or convert the section file
Paste HTML into /converter or hand-write Liquid following /shopify-liquid-examples. Output lands in sections/your-module.liquid with valid schema JSON.
- 03
Add presets so the section is discoverable
Without presets, custom sections never appear in Add section. Seed default blocks and settings so the first preview matches the approved design.
- 04
Register on the JSON template
Add a unique key in sections, reference type matching the filename, append the key to order. Commit template JSON with the section file in the same PR.
- 05
Editor QA before client review
Reorder sections, add a block row, clear optional images, preview mobile. Architecture only matters if merchants can operate it without filing tickets.
Architecture mistakes we still see on migrations
- Editing layout/theme.liquid for content that should be a section—merchants cannot touch it without code deploys.
- Duplicate section keys in JSON templates—Shopify rejects or overwrites silently depending on context.
- Mismatch between section type string and filename—type image-banner requires sections/image-banner.liquid.
- Shipping sections without presets on OS 2.0 builds—merchants think the feature is missing.
- Mixing app block slots and custom HTML in the same wrapper without {% case block.type %}—rendering breaks when apps are installed.
- Storing forty section-level settings when blocks would reduce sidebar scroll fatigue.
- Forgetting disabled_on or enabled_on in schema—sections appear on templates where they make no sense.
- Relying on include instead of render in new code—variable leaks cause subtle bugs across snippets.
Production standards for section architecture
One section file should do one job on the storefront: featured collection row, not featured collection plus newsletter plus reviews. JSON template keys should read like layout labels—hero_summer, faq_shipping—not section_1. Keep order arrays short on product templates; above-the-fold performance suffers when merchants stack twelve sections because nothing prevented it. Use section groups for true globals only—header, footer, optional overlay group—not every reusable band. Document in handoff which sections are static in JSON versus merchant-addable via presets. Align with /shopify-schema-guide on setting types so the editor stays approachable. When multiple developers touch the same theme, enforce a CODEOWNERS path on templates/ and sections/ to reduce JSON merge conflicts.
JSON template: how instances reference sections
templates/index.json excerpt
{
"sections": {
"hero_campaign": {
"type": "promo-hero",
"settings": {
"heading": "New arrivals",
"button_label": "Shop now",
"button_link": "shopify://collections/frontpage"
}
},
"featured_grid": {
"type": "featured-collection",
"settings": {
"collection": "frontpage",
"products_to_show": 4
}
}
},
"order": ["hero_campaign", "featured_grid"]
}Keys in sections are instance ids; type must match the section filename without .liquid.
Section file anatomy
Minimal section.liquid structure
{% comment %} sections/promo-hero.liquid {% endcomment %}
<div class="promo-hero" id="PromoHero-{{ section.id }}">
{%- if section.settings.heading != blank -%}
<h2>{{ section.settings.heading }}</h2>
{%- endif -%}
</div>
{% stylesheet %}
#PromoHero-{{ section.id }} { /* scoped rules */ }
{% endstylesheet %}
{% schema %}
{ "name": "Promo hero", "settings": [], "presets": [{ "name": "Promo hero" }] }
{% endschema %}Markup, optional {% stylesheet %}, and schema live in one file—the OS 2.0 unit merchants interact with.
Presets: how merchants discover custom sections
Preset with seeded blocks
"presets": [
{
"name": "FAQ shipping",
"blocks": [
{ "type": "faq_item", "settings": { "question": "Where do you ship?", "answer": "<p>We ship worldwide.</p>" } },
{ "type": "faq_item", "settings": { "question": "How long does delivery take?", "answer": "<p>3–5 business days.</p>" } }
]
}
]Presets appear in Add section; blocks inside presets teach merchants how repeatable rows work.
Section groups, app blocks, and dynamic placement
Header and footer section groups in JSON reference shared section instances across templates. When you add an announcement bar, confirm whether the parent theme expects it in header-group.json or as a normal homepage section—Dawn-derived themes differ from legacy forks. App blocks use @app in schema and let merchants insert review widgets or forms without theme code. Your custom HTML blocks and app blocks often share one loop; branch on block.type and reserve explicit slots for apps. Dynamic placement means merchants add sections via the editor; static placement means you commit template JSON. Both are valid—mix them intentionally. Document which custom sections are preset-only for marketing teams versus developer-locked in JSON for compliance content.
Static versus dynamic placement in practice
Static placement suits modules that must exist on every product page—main-product, apps wrapper, related products. You add them to product.json once; merchants configure settings but cannot remove the section without developer access. Dynamic placement suits marketing bands merchants rearrange weekly—hero, testimonial strip, featured collection. Those sections need presets and clear names in the Add section panel. Problems arise when teams statically embed a promo section in JSON but also ship a preset with the same purpose—merchants see duplicates and file bugs. Pick one ownership model per module. Migration projects from Vintage to OS 2.0 often start by statically placing converted sections where old {% section %} tags lived, then gradually expose presets once schema stabilizes.
Reading template JSON in the code editor
Shopify's admin code editor validates JSON templates on save. Trailing commas and duplicate keys fail the save—use a local JSON linter before push. Section instance keys must be unique within a template file; reuse the same type string across different keys when you need two instances of one section—summer_hero and winter_hero both type promo-hero. The settings object stores merchant values; blocks arrays store block instances with their own settings. When debugging missing sections on the storefront, trace three files: does the section liquid file exist, does the template reference the correct type string, is the instance key listed in order? Theme check CLI catches missing templates and invalid schema faster than clicking through the admin. Pair this debugging habit with /shopify-liquid-for-beginners when nil object errors appear—usually wrong template context, not broken section architecture.
Handoff checklist for non-technical stakeholders
- List section names as merchants see them in Add section—not internal filenames.
- Mark which sections are locked in JSON versus reorderable.
- Note block-based sections and recommended row counts.
- Link to /shopify-section-tutorial for adding a new band without developer help.
- Record which templates were updated—index, product, collection, page.
Performance, rendering cost, and schema weight
Each section in order renders on every page load for that template. Stacking twenty sections on index.json increases Liquid execution and DOM size even when merchants never scroll. Use schema enabled_on to keep specialized sections off irrelevant templates—quiz modules on pages only, not cart. Large block counts inside one section multiply HTML faster than spreading features across focused sections. JSON template files are parsed server-side; keep them valid JSON and avoid comments. Theme editor performance degrades when a single section exposes fifty settings—split into blocks or child sections. For product templates, keep main-product as the canonical purchase section; satellite sections should lazy-load media where possible. Rendering concerns pair with /shopify-liquid-examples for loop limits and with /dynamic-shopify-blocks-guide when block counts grow.
- Maintainability: one section per layout concern; JSON keys named for humans.
- Theme editor: presets, grouped settings, disabled_on for wrong templates.
- Rendering: shorter order arrays on high-traffic templates.
- Schema organization: settings for module chrome; blocks delegated to the blocks guide.
Practical summary
Sections are how OS 2.0 themes stay merchant-editable without constant deploys. JSON templates declare which sections render; section files declare how they render and what merchants can change. Master presets and template registration before optimizing Liquid tricks. Build new modules with /converter or patterns from /shopify-liquid-examples, validate schema against /shopify-schema-guide, and walk through /shopify-section-tutorial when onboarding your first JSON template change. Architecture is not abstract—it's the difference between a merchant who self-serves content updates and a merchant who emails you for every headline change.
Vintage theme refugees often underestimate how much product behavior moved into JSON. Spend your first OS 2.0 afternoon reading templates/index.json and sections/ side by side in the code editor—do not start by editing layout.liquid. Draw a one-page diagram for each client: boxes for sections, arrows for order, notes for which boxes are static versus preset-driven. That diagram becomes the handoff document account managers understand. Update it when you add a section; stale architecture docs cause duplicate modules and conflicting promo bands. When in doubt, add a section instead of hardcoding in theme.liquid—future merchants will not thank you for strings buried in layout files.
Relationship to blocks and examples
This guide explains where sections live in the theme. /dynamic-shopify-blocks-guide explains repeatable rows inside a section. /shopify-liquid-examples shows Liquid shapes for heroes, grids, and FAQs. None of the three replaces the others—they stack. Architecture first, blocks second, syntax third. A developer who jumps straight to examples without JSON template context ships valid Liquid in the wrong file or under the wrong template key. Read sections architecture before copying loops.

