Every Shopify store has the same building blocks — but not every store uses them the same way. If you have ever wanted to add a unique layout, a custom promotional banner, or a feature-rich content module that no app or default theme can deliver, building custom Shopify sections is the path forward. With the introduction of Shopify Online Store 2.0, sections became the universal unit of the entire theme system, opening up a level of flexibility that simply did not exist before.
This guide walks through everything you need to know: how sections work architecturally, how to write a schema, how to build a Liquid template, and the patterns that separate well-built sections from brittle ones. Whether you are a developer picking up Shopify for the first time or a technical merchant who wants to understand what your agency is building, this is the complete reference.
Understanding Shopify Sections Architecture
How Sections Fit Into the Shopify 2.0 Theme Architecture
Before Online Store 2.0, sections were limited to the homepage. Every other page — product pages, collection pages, blog posts — used rigid, hardcoded templates. Shopify 2.0 sections changed everything. Any page template in a 2.0 theme is now a JSON file that references a list of sections, and merchants can add, remove, and reorder those sections directly from the Theme Editor without touching code.
This means sections are no longer layout helpers; they are the primary authoring primitive. A product page is just a JSON template file pointing to a header section, a product-info section, a related-products section, and so on. Every piece of the page is a section.
Under the hood, Shopify themes store section files in the sections/ directory. Each file is a .liquid file that contains both the HTML/Liquid markup and a {% schema %} tag at the bottom. That schema is what the Theme Editor reads to render controls. When the merchant saves a customization, Shopify writes those values into the template JSON, which the section reads at render time.
Sections vs Blocks vs Snippets
Three terms trip up almost every newcomer. Here is the distinction:
- Sections — Standalone, reusable layout modules — the foundation of custom Shopify sections that live in
sections/. They have their own schema and can be placed on any page. Each section renders independently. - Blocks — Repeatable sub-components within a section. A testimonials section might have a block for each individual testimonial. Merchants can add, reorder, and remove blocks inside a section without writing code.
- Snippets — Reusable Liquid partials stored in
snippets/. They have no schema and cannot appear in the Theme Editor on their own. They are included with{% render %}and are best used for shared markup logic, not for editable content areas.
A well-architected set of custom Shopify sections uses blocks for repeatable content (slides in a carousel, items in a features grid, steps in a process list) and delegates reusable markup to snippets. The section schema only exposes the controls that make sense for a merchant to change.
Anatomy of a Custom Shopify Section
The Liquid Template Layer
The Liquid portion of a section file is standard HTML with Liquid objects and tags woven in. The section's settings are accessed via section.settings, and its blocks are iterated with a for loop over section.blocks. Every block exposes its own settings under block.settings.
A minimal feature-list section might look like this:
<div class="feature-list" id="shopify-section-{{ section.id }}">
{% if section.settings.heading != blank %}
<h2>{{ section.settings.heading | escape }}</h2>
{% endif %}
<ul class="feature-list__items">
{% for block in section.blocks %}
<li class="feature-list__item" {{ block.shopify_attributes }}>
<strong>{{ block.settings.title | escape }}</strong>
<p>{{ block.settings.description }}</p>
</li>
{% endfor %}
</ul>
</div>A few things worth noting in the template above:
id="shopify-section-{{ section.id }}"— Shopify injects this class automatically on the outer wrapper, but adding a matchingidlets you scope CSS precisely.{{ block.shopify_attributes }}— This outputs the data attributes Shopify needs to make block-level drag-and-drop work inside the Theme Editor. Always include it on the block's root element.- Empty checks like
{% if section.settings.heading != blank %}— Never output empty elements. If a merchant leaves a field blank, the markup should not render that element at all.
The Schema JSON Layer
The {% schema %} tag is where a section declares what the Theme Editor should render. It is a JSON object containing the section's name, settings, blocks, and presets. This is the heart of the Shopify section schema.
Here is the schema for the feature-list section above:
{% schema %}
{
"name": "Feature List",
"tag": "section",
"class": "feature-list-section",
"settings": [
{
"type": "text",
"id": "heading",
"label": "Section heading",
"default": "Why choose us"
}
],
"blocks": [
{
"type": "feature_item",
"name": "Feature",
"settings": [
{
"type": "text",
"id": "title",
"label": "Feature title",
"default": "Fast delivery"
},
{
"type": "textarea",
"id": "description",
"label": "Description",
"default": "Ships in 2 business days."
}
]
}
],
"presets": [
{
"name": "Feature List",
"blocks": [
{ "type": "feature_item" },
{ "type": "feature_item" }
]
}
],
"max_blocks": 12
}
{% endschema %}Key schema properties to understand:
| Property | Purpose | Required? |
|---|---|---|
name | Display name shown in the Theme Editor sidebar | Yes |
settings | Section-level controls (text, image, color, etc.) | No |
blocks | Repeatable child elements with their own settings | No |
presets | Makes the section available in the "Add section" menu | No, but recommended |
max_blocks | Limits how many blocks a merchant can add | No |
disabled_on | Hides section from specific template types | No |
The available input types for settings include text, textarea, richtext, image_picker, url, color, range, select, checkbox, product, collection, and blog, among others. Choosing the right type matters — a richtext field lets merchants format text; a plain text field does not.
CSS and JavaScript Considerations
Sections can embed their own <style> and <script> tags directly in the file. Shopify deduplicates these across multiple instances of the same section on a page, so you do not need to worry about styles loading twice. However, there are trade-offs:
- Inline
<style>— Good for section-specific overrides. Keep them scoped with the#shopify-section-{{ section.id }}selector to avoid leaking styles to other sections. - Inline
<script>— Acceptable for small, self-contained interactions. For anything complex, load a separate JS file via{{ 'section-feature-list.js' | asset_url | script_tag }}so it can be cached by the browser. - Theme stylesheet — For styles shared across sections, add them to the main theme CSS rather than embedding them in every section file.
Step-by-Step: Building Your First Custom Section
1. Define the Purpose and Layout
Before writing a single line of Liquid, answer two questions: What content does this section display, and what should a merchant be able to change? Sketch the layout — even a rough wireframe on paper will reveal whether you need blocks (repeatable rows) or just section-level settings (one-off controls). A testimonials carousel needs blocks. A simple hero banner does not.
2. Write the Schema
Start with the schema, not the template. Define every setting and block type you will need, assign sensible defaults, and check the merchant experience in your mind: Can a non-technical person understand these labels? Are there too many options? As a rule, expose only the controls that genuinely need to change. A developer can always add more settings later; a cluttered editor panel frustrates merchants.
When building shopify liquid sections for a client store, it is common to include a color_scheme setting tied to the theme's native color scheme picker. This lets merchants match the section's background to the rest of the theme without touching CSS:
{
"type": "color_scheme",
"id": "color_scheme",
"label": "Color scheme",
"default": "scheme-1"
}3. Build the Liquid Template
With the schema locked, write the markup. Reference every setting you declared in step 2. Use conditional logic to handle blank values gracefully. Wrap the outer container in the color-scheme color-{{ section.settings.color_scheme }} classes if you are using the native color scheme system.
Avoid hardcoding any content that a merchant might reasonably want to change. Hardcoded strings in templates create support tickets. Every piece of merchant-facing text should come from a setting or a translation file (locales/en.default.json).
4. Add Styles
Scope all CSS to the section. If the section is unlikely to appear more than once per page, use #shopify-section-{{ section.id }}. If you are building a reusable module that could appear multiple times, use a BEM-style class name like .feature-list__item and keep styles generic enough to work in any context.
Test at 320 px, 768 px, and 1280 px viewports before calling the section complete. Mobile responsiveness issues are the single most common problem in custom shopify theme sections.
5. Register and Test
Drop the completed custom Shopify sections file — the .liquid file into the sections/ directory. Open the Theme Editor, navigate to a page that uses a JSON template, and click "Add section." Your section should appear under its preset name. Add it, configure the settings, and verify that all controls work as expected.
Test with empty settings (remove all default text and images) to confirm the section degrades gracefully. Then test with the maximum number of blocks to confirm layout does not break at scale.
Advanced Section Patterns
Dynamic Sources and Metafields
One of the headline features of Shopify 2.0 sections is dynamic sources. Settings that support it — text fields, image pickers, URLs — can be connected to metafields, metaobjects, or product data directly in the Theme Editor, with no Liquid changes required. To enable this, add "dynamic_sources": true to individual settings in your schema (this is handled automatically for most setting types in Shopify's latest Dawn-based themes).
For more granular control, you can access metafields directly in Liquid:
{{ product.metafields.custom.feature_badge.value }}Pair metafield access with fallback values so the section renders correctly even when the metafield is not set on every product.
Nested Blocks
As of Shopify's 2024 updates, sections support nested blocks — blocks that contain their own child blocks. This is useful for complex layouts like tabbed interfaces (tabs are blocks; tab content items are nested blocks) or accordion FAQs. The schema syntax uses a blocks array inside a block definition, and Liquid accesses children via block.blocks.
Section-Specific App Blocks
If you are building a section that should integrate with apps — reviews, loyalty widgets, upsell tools — use the @app block type to create an app block slot:
{
"type": "@app"
}Including this in your section's block definitions lets merchants drag app blocks into your custom section from the Theme Editor, without requiring the app to build its own full-width section.
Performance Considerations
Sections that load external scripts or large images on every page view can significantly affect Core Web Vitals. Follow these principles:
- Use
loading="lazy"on all images that are below the fold. - Defer or async-load JavaScript that is not needed for the initial render.
- Avoid render-blocking CSS. If a section's styles are unique and large, consider lazy-loading them with a
<link rel="preload">strategy. - Use Shopify's
image_urlfilter with explicit width parameters to serve correctly sized images:{{ image | image_url: width: 800 }}.
Common Mistakes When Building Custom Sections
Schema Errors That Break the Editor
Invalid JSON in a {% schema %} block causes the Theme Editor to fail silently or throw a generic error. When building custom Shopify sections, always validate your schema JSON in a linter before uploading. Common mistakes include trailing commas after the last item in an array or object, mismatched quotes, and duplicate setting IDs within the same schema. Every id in a section's settings and blocks must be unique within that section.
Performance Pitfalls
For custom Shopify sections, the most damaging performance mistake is including a JavaScript library — a slider, a chart, an animation library — inside a section file that is loaded on every page, even pages where the section does not appear. Load scripts conditionally, and prefer native browser APIs (Intersection Observer for scroll animations, CSS scroll snap for carousels) over heavyweight libraries when possible.
Mobile Responsiveness
Custom custom shopify sections often look correct on a desktop preview in the Theme Editor but break on real mobile devices. The Theme Editor's mobile preview is not a reliable substitute for testing on actual hardware. Pay particular attention to horizontal overflow (elements wider than the viewport), touch targets smaller than 44×44 px, and font sizes below 16 px that trigger iOS zoom on input focus.
Missing Shopify Attributes on Blocks
Forgetting {{ block.shopify_attributes }} on the root element of each block makes block-level interactions in the Theme Editor (clicking a block to select it, drag-and-drop reordering) stop working. It is an invisible bug during development but an obvious UX problem for merchants.
When to Build Custom vs Use Existing Sections
Not every layout problem requires custom Shopify sections. Here is a straightforward decision matrix:
| Scenario | Recommendation |
|---|---|
| Standard layout (hero, features grid, testimonials) that matches theme style | Customize an existing section via settings |
| Third-party app provides the functionality | Use app embed blocks or the app's native section |
| Layout has unique structure not present in theme | Build a custom section |
| Content is highly dynamic and changes per product/page | Build a custom section with metafield support |
| Multiple stores need the same section | Build a custom section as a shared component |
| One-off promotional layout, used once | Hardcode a template — do not over-engineer |
The key principle for custom Shopify sections is that Shopify theme development investment should match the longevity and reuse of the component. A custom section that will be maintained for two years and replicated across multiple stores is worth building properly. A one-week sale banner is not.
When a custom section is the right call — whether for a complex interactive layout, a brand-specific content module, or a reusable component across a multi-store setup — the work pays dividends in merchant autonomy. Once built, a merchant can manage the section's content without filing a support request every time a headline changes.
Conclusion
Building custom Shopify sections is one of the highest-leverage skills in the Shopify development ecosystem. A well-constructed section combines a clean Liquid template, a thoughtfully scoped schema, and defensive CSS — giving merchants full control over their content without exposing them to code. With the foundations of Shopify 2.0 sections in place, the ceiling on what a Shopify theme can do is essentially defined by your Liquid and JavaScript skills, not by the platform itself.
Start with a single, focused section. Nail the schema, test at every breakpoint, and iterate on the merchant experience as much as the code. From there, patterns like nested blocks, dynamic sources, and metafield integrations follow naturally.
If your store needs bespoke functionality that goes beyond what themes and apps can provide out of the box, our team specializes in custom Shopify sections development — from initial architecture through to long-term maintenance. Get in touch to discuss what you are building.
FAQ
What are custom Shopify sections used for?
Custom Shopify sections let developers build unique layouts and content modules that standard theme blocks or apps cannot provide. In Shopify 2.0, they can be added, removed, and reordered in the Theme Editor.
How do Shopify 2.0 sections differ from older Shopify templates?
In Shopify 2.0, page templates are JSON files that reference sections, so merchants can manage layout directly in the Theme Editor. Older templates were hardcoded and sections were mostly limited to the homepage.
What is the role of the Shopify section schema?
The shopify section schema is the JSON inside {% schema %} that tells the Theme Editor which settings, blocks, presets, and limits a section supports. It defines the editable options for the section.
When should a section use blocks instead of settings?
Use blocks for repeatable items like testimonials, slides, or feature rows. Use section settings for one-off controls like a heading, background color, or layout choice in a custom Shopify section.
Why should block.shopify_attributes be included in custom Shopify sections?
Including {{ block.shopify_attributes }} keeps Theme Editor interactions working, including block selection and drag-and-drop reordering. Without it, blocks may still render but editor controls can break.


