2026March & April tutorial updates are live →

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

Settings apply to the whole section once—heading, background color, section-wide layout toggle. Blocks are repeatable child units merchants reorder—testimonials, features, FAQ items. Use settings for global config; blocks for lists. ADSPOC avoids fake blocks when only one instance exists—use settings instead to simplify the editor.

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