How to Build Custom Shopify Sections (Step-by-Step)
Custom sections are the core of Online Store 2.0. This step-by-step guide walks through building one from scratch—markup, schema, styles, JavaScript, and theme editor integration—the way ADSPOC ships production sections.
Step 1: Plan the section and create the file
Start with merchant requirements and editor UX: what should be draggable, what needs blocks, what stays fixed? Create sections/feature-grid.liquid (filename becomes type: feature-grid in JSON templates). ADSPOC section briefs list settings (text, images, toggles), blocks (repeatable items), max blocks, and mobile behavior before code—preventing schema rework mid-build.
At the top of the file, wrap markup in a semantic container with a section-specific class and id: <section id="shopify-section-{{ section.id }}" class="feature-grid section-{{ section.id }}" data-section-id="{{ section.id }}">. The section.id hook is mandatory for Section Rendering API re-renders and JavaScript scoped queries. Load assets with {% stylesheet %} and {% javascript %} tags (Shopify 2.0 asset pipeline) or dedicated asset files referenced once—avoid duplicate script tags when multiple section instances exist.
Step 2: Write Liquid markup with settings and blocks
Output settings with {{ section.settings.heading }} and escape user HTML intentionally—use | escape unless the setting is richtext rendered with {{ section.settings.body }}. Loop blocks: {% for block in section.blocks %} <div {{ block.shopify_attributes }}> ... </div> {% endfor %}. block.shopify_attributes adds theme editor overlay hooks—never omit on block wrappers.
Branch on block.type when one section supports multiple block types: {% case block.type %}{% when 'feature' %}...{% when 'stat' %}...{% endcase %}. Use {% render %} for repeated block innards. Empty states: {% if section.blocks.size == 0 %} show placeholder in Design Mode only with {% if request.design_mode %} so live storefronts do not render 'Add blocks' text to customers.
Step 3: Define the {% schema %} for the theme editor
Schema is JSON inside {% schema %} ... {% endschema %} at the bottom of the section file. Required keys: name, settings, blocks (optional), presets (optional), and tag (section vs header/footer wrappers). Setting types include text, textarea, richtext, image_picker, url, color, range, checkbox, select, collection, product, blog, page, and header (visual grouping only). Each setting needs id, type, label, and default where applicable.
Blocks declare type, name, and their own settings array. presets seed new section drops in the editor: "presets": [{ "name": "Feature grid", "blocks": [{ "type": "feature" }, { "type": "feature" }] }]. limit on blocks caps count. ADSPOC always adds translated labels via t: keys in locale files for multi-market themes—"label": "t:sections.feature_grid.settings.heading.label"—keeping Admin strings maintainable across en.default.json and regional locales.
Step 4: Style the section with scoped CSS
Scope styles to .section-{{ section.id }} or use CSS variables set inline from settings: style="--heading-color: {{ section.settings.heading_color }}". Inline section-scoped variables let merchants pick colors in schema without bloating global CSS. Prefer {% stylesheet %} blocks for co-located CSS—Shopify bundles and deduplicates on compile.
Mobile-first breakpoints: base styles for small screens, min-width media queries for desktop. Avoid !important unless fighting third-party apps. Respect Shopify's grid spacing conventions—section padding settings (padding_top, padding_bottom) as range inputs are ADSPOC standard on every section so merchants tune whitespace without code. Test in theme editor preview, iOS Safari, and with content extremes—long Hindi headings, missing images, single block vs max blocks.
Step 5: Add JavaScript hooks and register the section
Use {% javascript %} for section-specific behavior—carousels, tabs, accordions—or a theme module imported once in theme.liquid for shared utilities. Initialize with a class pattern: document.querySelectorAll('[data-feature-grid]').forEach(el => new FeatureGrid(el)). Pass {{ section.id }} into data attributes for Section Rendering API events: document.addEventListener('shopify:section:load', e => { if (e.detail.sectionId === '...') reinit }).
Register the section in JSON templates: add a key to sections and order in product.json or index.json. Ship a preset so merchants find it under Add section. Document the section in internal theme docs with screenshot, intended placement, and performance notes (e.g., max 6 blocks, images 800px wide). ADSPOC QA checklist: schema validates (Shopify CLI theme check), keyboard accessible, works with app blocks if enabled in schema "blocks": [{ "type": "@app" }], and survives section reorder without JS errors.
Frequently asked questions
Get a free conversion audit from India's best Shopify builders
ADSPOC since 2000 · India's #1 CRO-focused Shopify agency · any store type · 18-day delivery or money back · 23+ conversion features built in · WhatsApp direct line · trained thousands of developers · Mumbai & Solan, serving India, Bangladesh, Pakistan, and worldwide.
Prefer a quick chat? Message ADSPOC on WhatsApp
Related reading
JSON Templates vs Liquid Templates in Shopify
Online Store 2.0 replaced monolithic Liquid templates with JSON templates that compose sections. Learn when to use each format, how JSON templates are structured, and how alternate templates unlock flexible merchandising.
Shopify Snippets: Reusable Liquid Code Patterns
Snippets are the composable building blocks of every ADSPOC theme. Learn render tag syntax, parameter passing, snippet architecture, and battle-tested patterns for icons, product cards, and beyond.
Liquid Filters and Tags: The Developer's Reference
Filters transform output; tags control logic. This reference covers the Liquid filters and tags Shopify theme developers use every day—with syntax, gotchas, and ADSPOC production patterns.