Tutorial

Shopify section tutorial for Online Store 2.0

A practical walkthrough of how Shopify sections work in modern themes—from JSON templates and section groups to shipping custom sections your merchants can edit.

12 min read · Published 2026-06-08

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

Knowledge Hub

What you will build in this tutorial mindset

This tutorial explains the full lifecycle of a Shopify section: design HTML, convert to Liquid with schema, add the file to your theme, register it on a JSON template, and let merchants configure it in the editor. Whether you work on Dawn, Prestige, or a custom client theme, the OS 2.0 section model is the same.

Online Store 2.0 fundamentals

Online Store 2.0 replaced static Liquid templates with JSON templates that list sections in order. Each JSON file—index.json, product.json, collection.json—declares which sections appear and their default settings. Merchants reorder and configure sections without code access.

  • Sections are .liquid files in the theme sections/ directory.
  • Templates are .json files in templates/ that reference sections by type.
  • App blocks let apps inject content into designated section slots.
  • Section groups bundle header and footer sections across templates.

Anatomy of a section file

A production section file contains three parts: optional scoped CSS in a style tag, Liquid markup using section.settings and block loops, and a schema block defining editor controls. The outer wrapper should be a single section element with a meaningful class prefix.

Section file structure

<section class="custom-hero">
  <h1>{{ section.settings.heading }}</h1>
</section>

{% schema %}
{ "name": "Custom hero", "settings": [], "presets": [] }
{% endschema %}

Step 1 — Start from HTML

Export markup from Figma, Tailwind, or your component library. Paste it into /converter or the /shopify-section-generator. The tool maps static values to settings, detects repeatables for blocks, and scopes CSS to section.id.

Step 2 — Choose Flat or Block mode

Flat mode suits fixed heroes and announcement bars—content lives in section.settings. Block mode suits FAQs, sliders, and card grids where merchants add rows. /how-shopify-sections-work explains when each mode fits your architecture.

Step 3 — Add the section to your theme

Save the exported file as sections/your-section.liquid in your theme repository. Commit via Git or upload through the theme editor code view. Verify schema validates—invalid JSON prevents the section from appearing.

Step 4 — Register on a JSON template

index.json section entry

{
  "sections": {
    "custom_hero": {
      "type": "custom-hero",
      "settings": { "heading": "Welcome" }
    }
  },
  "order": ["custom_hero"]
}

The type value must match the section filename without .liquid.

Dynamic sections and section groups

Dynamic sections can be added by merchants at runtime through the Add section panel—enabled by presets in schema. Section groups (header, footer) let themes define shared wrapper sections that persist across templates. When building header-adjacent banners, confirm whether your theme expects the section in a group or on individual JSON templates.

Testing and merchant handoff

  • Preview on mobile and desktop in the theme editor.
  • Confirm all settings and blocks render with empty defaults.
  • Document which templates include the section for client training.
  • Match typography tokens to the parent theme for visual consistency.

Next steps in the knowledge hub

Deepen schema skills with /shopify-schema-guide, study block patterns in /dynamic-shopify-blocks-guide, and browse copy-ready Liquid in /shopify-liquid-examples. Theme-specific deployment notes live in /html-to-dawn-theme and sibling theme guides.

Online Store 2.0 changed where you edit what

Before OS 2.0, templates were Liquid files with hardcoded includes. Merchants could edit some settings through the theme settings config, but page composition was a developer task. JSON templates inverted that: templates/*.json describe ordered section instances, each with a type and settings object. Developers own section files and default JSON; merchants reorder sections, toggle settings, and add preset-backed sections from the editor. Section groups—header-group.json and footer-group.json in Dawn—bundle shared chrome across templates. When you build a announcement bar, know whether the client expects it in the header group or as a standalone section on select templates. Putting it in the wrong layer means either global visibility you did not want or per-template duplication you did not plan. This tutorial's workflow assumes Git-backed theme development, but the same steps apply when uploading through the admin code editor—only the deploy mechanism differs.

Theme file relationships

  layout/theme.liquid
        │
        ├── section groups (header, footer)
        │       └── sections/*.liquid
        │
        └── JSON template (e.g. index.json)
                │
                ├── section instance "hero_1"
                │       type → sections/hero.liquid
                │       settings → { "heading": "..." }
                │
                └── order: ["hero_1", "featured_collection", ...]

JSON templates reference section types by filename. Instance keys are arbitrary; type must match sections/your-file.liquid without the extension.

Anatomy of a production section file

A mature section file usually opens with an optional {%- style -%} block that emits scoped CSS using section.id or section.settings values—padding ranges map to padding-top declarations here. The HTML wrapper is one root element with a BEM-style class prefix. Inside, Liquid outputs settings, loops blocks, and render calls snippets. The schema block is last. Some teams extract large schema into separate JSON during build, but Shopify expects schema inline at deploy time. Dawn sections demonstrate color_scheme_class helpers and spacing utilities; client themes often mirror those patterns so CSS variables stay consistent. Avoid loading external scripts per section instance—if three instances of your slider section load the same script, you triple network cost. Use asset_url in the theme layout or a single shared asset snippet loaded once. For images, prefer image_tag with widths that match your srcset strategy rather than raw img tags with hardcoded CDN paths.

Section wrapper with scoped styles

{%- style -%}
  #shopify-section-{{ section.id }} .promo {
    padding-top: {{ section.settings.padding_top }}px;
  }
{%- endstyle -%}

<section class="promo color-{{ section.settings.color_scheme }}">
  {% for block in section.blocks %}
    <div class="promo__item" {{ block.shopify_attributes }}>
      {{ block.settings.text }}
    </div>
  {% endfor %}
</section>

{% schema %}
{ "name": "Promo band", "settings": [], "presets": [{ "name": "Promo band" }] }
{% endschema %}

section.id scopes CSS so two promo sections on the same page do not fight each other.

Step-by-step: shipping a custom section to a Dawn fork

  1. 01

    Branch the theme repo and duplicate in admin

    Work on a unpublished theme copy named with the ticket id. Clients preview the duplicate URL while production stays untouched until sign-off.

  2. 02

    Convert HTML via /converter or /shopify-section-generator

    Choose block mode when markup repeats—FAQ rows, logo strips, timeline items. Flat mode for single-instance heroes. Download the combined Liquid file.

  3. 03

    Name the file and fix the schema

    Save as sections/client-name-component.liquid. Kebab-case matches Shopify conventions. Validate presets, max_blocks, and setting ids against /shopify-schema-guide.

  4. 04

    Insert into the target JSON template

    Add a unique key under sections, set type to your filename, seed settings, append the key to order. Commit template and section together so deploy is atomic.

  5. 05

    Theme editor QA and merchant Loom

    Test mobile preview, empty settings, and block reorder. Record a short walkthrough showing Add section, safe settings, and what not to touch.

JSON templates: instances, order, and app blocks

Each key under sections is an instance id—arbitrary but unique within the template. The type property maps to the Liquid filename. settings holds default values matching schema ids. blocks can appear on instances when the section supports dynamic blocks at the template level—less common for custom work than blocks added in the editor. order is an array of instance keys determining vertical stack. Removing a key from order without deleting the sections entry orphans config; deleting the entry cleans JSON. App blocks appear when schema defines @app blocks type or dedicated app block slots in compatible themes—know whether your client uses reviews or subscription apps that inject into main-product. Custom sections you build rarely host app blocks unless you plan for them. Merchants still expect your section to coexist visually with app-injected stars and badges—leave spacing tokens flexible.

Dynamic sections versus template-locked sections

Any section with presets can be added via Add section on JSON templates that allow dynamic sections—typically index and page templates. Product and collection templates often lock structure so merchants do not accidentally remove buy buttons. You can still add custom sections below main-product if the template JSON and theme architecture allow. Check the parent theme before promising unlimited Add section freedom on PDPs. Template-locked sections are hardcoded only in JSON—useful for compliance banners that must appear exactly once with developer-controlled placement. Communicate which mode you chose in handoff docs. Dynamic freedom helps marketing; locked structure protects revenue paths. Agency projects usually lock checkout-adjacent templates and liberalize homepage JSON.

Section groups and global chrome

Header and footer groups live in sections/ as JSON files referencing member sections. Editing header-group.json changes navigation across the storefront. Announcement bars, promo strips, and trust badges often belong here rather than on every page template individually. When your custom section includes sticky positioning, test against the header group's height and z-index stack—Dawn uses CSS variables like --header-height updated by JS. A sticky subnav that works on a standalone landing template may slide under the header on collection pages if you did not read the group layout. Some themes expose alternate headers per template through conditional section visibility; others duplicate header instances—ask before building. Footer groups follow the same rules for newsletter bands and payment icons.

  • header-group.json — typically announcement + header; z-index wars start here
  • footer-group.json — newsletter, links, localization; mind long locale lists on mobile
  • JSON templates never duplicate group content—they reference layout that pulls groups in
  • Custom sections for global chrome should be rare; prefer editing existing group members
  • See /html-to-dawn-theme for Dawn-specific group file names in recent versions

Performance and editor limits merchants never see

Each section instance is additional Liquid execution and DOM weight. Homepage templates with twenty sections feel sluggish in editor preview long before Lighthouse complains. Cap block counts, lazy-load images below the fold, and avoid O(n²) Liquid—nested loops over all products inside every collection tab is a classic agency mistake. Use limit on for loops and fetch collection data once with assign outside the loop when possible. Defer non-critical JS to intersection observers in assets, not inline script per section. In the editor, heavy sections slow click-to-select responsiveness—merchants blame the platform when the real issue is unbounded blocks. Document recommended maximum content—six logos, eight FAQs—in help_text. Performance is part of UX; schema max_blocks is a legitimate tool, not a limitation to apologize for.

Handoff: what clients need beyond the code

Ship a one-page doc listing each custom section preset name, screenshot, intended templates, and which settings are safe for marketing to edit. Link /shopify-section-tutorial and /shopify-liquid-for-beginners for team members who want self-serve depth. Include the duplicate theme URL, the Git branch name, and the publish checklist—run theme check, verify JSON templates, confirm apps enabled. Merchants search 'FAQ accordion' in Add section, not faq-module.liquid. Preset names are your API to non-technical users. When you return for phase two, stable section types in JSON templates mean new settings can be added to schema without rewriting template instances—as long as ids stay backward compatible. That is why tutorial workflows emphasize atomic commits: section Liquid, schema, and template JSON together, tested in the editor before merge to main.

Common tutorial mistakes we still see in client repos

Forgetting block.shopify_attributes is the fastest way to make a section feel broken in the editor while the live site looks fine. Shipping schema without presets when the brief said merchants need Add section access. Hardcoding collection handles in Liquid instead of collection pickers—merchants cannot swap collections without a developer. Putting global element selectors in section CSS that override Dawn button styles sitewide. Registering a section on index.json but not adding the instance key to order, which silently drops the section from the stack. Testing only on desktop preview when ninety percent of merchant edits happen on mobile. Naming section files with underscores when JSON type expects kebab-case—Shopify is forgiving in some cases, inconsistent in others. Running /converter once and never reconciling output with the parent theme's color_scheme and spacing tokens. Each mistake is five minutes to fix in week one and five hours to untangle in year two.

Version control discipline matters as much as Liquid skill. Commit section Liquid, schema, and template JSON in the same pull request so reviewers see the full deploy unit. Tag the admin theme duplicate URL in the PR description—reviewers should click preview, not imagine layout. When Shopify CLI theme dev is in your stack, use it for hot reload against the duplicate theme; it catches JSON syntax errors faster than save-and-refresh in the browser. The tutorial mindset is iterative: ship a thin section that works, then add schema settings in phase two once stakeholders confirm content structure.

Agency delivery timeline for a new section

On client projects we budget section delivery in four phases: intake and HTML approval, converter export and schema refinement, theme integration and JSON template registration, editor QA and merchant training. Intake captures whether the module is global or template-specific, which breakpoints matter, and whether repeatables are blocks. Converter export happens per module—not one giant homepage paste—so filenames and presets stay coherent. Integration includes Dawn color_scheme alignment, checking adjacent sections for CSS collisions, and verifying app blocks still fit if the client runs reviews or subscriptions apps. QA is editor-first: mobile preview, empty states, max blocks, reorder stress test. Training is a five-minute Loom showing Add section and which settings marketing owns. This timeline is what /shopify-section-tutorial encodes as steps; skipping a phase is how sections land on live themes with missing presets or wrong template keys.

When you maintain a library of sections across clients, standardize preset names and setting id prefixes per brand slug only when necessary—otherwise prefer descriptive generic names reusable on the next storefront. Keep a internal Notion or spreadsheet mapping Figma component names to section types; project managers reference that sheet when they say fix the hero. The tutorial is not a one-time read; revisit Step 4 whenever Shopify updates JSON template validation or when you onboard a junior developer who has only edited Vintage themes. OS 2.0 is the default expectation on new builds; your workflow should assume JSON templates, block loops, and editor QA on every delivery without renegotiating process per ticket.

Section tutorials matter most when teams disagree about where logic lives. Product metafields belong in Liquid reads, not hardcoded in snippets. Cart drawer behavior belongs in theme JavaScript assets with clear section hooks, not inline onclick attributes copied from a React prototype. App blocks belong in designated slots, not hacked into random sections because the deadline is tonight. This tutorial's steps are the arbitration document: HTML becomes a section file, schema defines merchant control, JSON templates define placement, editor QA defines done. Dhruv enforces that split on design-heavy projects; Nishad enforces it on migration-heavy projects where legacy includes tempt everyone to patch instead of rebuild.

After your first successful section deploy, duplicate the workflow for a block-based FAQ—it exercises loops, max_blocks, and reorder UX in one module. That second section teaches more than reading ten tutorials because you feel editor constraints directly. Save the FAQ as a preset template your agency reuses on the next storefront rebuild.

JSON template mistakes that pass code review but fail in admin

Valid Git diffs still break in the theme editor. Duplicate instance keys in templates/index.json cause save failures — every key under sections must be unique. Listing a section in sections but omitting its key from order silently drops it from the storefront stack. Typo in type (sections/promo-banner.liquid registered as promo_banner) means Shopify cannot resolve the file. Trailing commas in JSON templates fail admin save with errors juniors misattribute to Liquid. Before requesting merchant review, open the template in admin code editor, save once, and confirm no red error banner. If save succeeds but the section is missing, trace order[], type string, and whether the section file exists on the branch connected to that theme.

  • Instance key in order[] but missing from sections{} — add the object or remove the key
  • type string mismatch — filename is kebab-case without .liquid
  • Seeded settings use ids that no longer exist in schema — update JSON or restore ids
  • Section added only to Git, not pushed to the duplicate theme the client previews

Frequently asked questions

Do I need a preset for every custom section?

Yes for sections merchants add via the editor. Presets surface your section in the Add section list. JSON-template-only sections can work without presets if you hardcode them in template JSON.

Can merchants break layout with too many blocks?

They can add many rows. Use max_blocks in schema to cap repeatables and keep editor performance acceptable.

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.