Architecture

How Shopify sections work

A clear explanation of Shopify section architecture—JSON templates, schema, blocks, presets, and section groups—for developers building Online Store 2.0 themes.

10 min read · Published 2026-06-08

Share with Shopify developers — useful guides spread faster in theme dev communities.

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

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

Frequently asked questions

What is the difference between a section and a snippet?

Sections have schema and appear in the theme editor. Snippets are reusable Liquid partials without schema—loaded via {% render %} for cards, icons, and shared markup.

Can one section appear on multiple templates?

Yes. Add the section type to any JSON template order array, or let merchants insert it dynamically where presets allow.

How do app blocks relate to sections?

Sections can declare @app block types in schema. Apps provide blocks merchants place inside those slots—reviews, forms, trust badges—without theme code changes.

What is the order array in JSON templates?

The order array lists section instance keys top to bottom. Shopify renders sections in that sequence on the storefront. Reordering in the editor updates this array.

Why are presets required for custom sections?

Presets register sections in the Add section panel. Without a preset, merchants can only use sections you statically embed in JSON—they cannot add your module themselves.

What is a section group versus a template section?

Section groups bundle shared areas like header and footer across the storefront. Template sections belong to a specific page JSON file such as index.json or product.json.

Can I still use Liquid template files on OS 2.0?

JSON templates are standard for OS 2.0. Legacy .liquid template files exist on older themes but new work should target JSON templates and sections.

How does section.settings reach the Liquid file?

When a merchant saves the editor, Shopify stores values in template JSON. At render time, those values populate section.settings inside the matching section.liquid file.

Editorial review

Reviewed by the HTML to Liquid Converter team

Dhruv Goyani, Shopify Developer at HTML to Liquid Converter

Dhruv Goyani

3 years web design + 3 years Shopify development experience

LinkedIn profile →
Nishad Kikani, Lead Shopify Developer at HTML to Liquid Converter

Nishad Kikani

2 years web design + 6 years Shopify development experience

LinkedIn profile →

Content is reviewed by the HTML to Liquid Converter Shopify development team before publication. Technical accuracy is validated against current Shopify Online Store 2.0 conventions and active client theme work.

Last updated:

Questions or corrections? Contact us.

Share

Share this guide

Found this useful? Share it with other Shopify developers.