ADSX
FEBRUARY 21, 2026

Creating Custom Shopify Sections: Developer Guide to Theme Customization

Master Shopify custom sections with this comprehensive guide. Learn schema architecture, dynamic content integration, and best practices for building reusable theme components. Includes code examples and implementation strategies.

AUTHOR
AT
AdsX Team
E-COMMERCE SPECIALISTS
READ TIME
13 MIN

Creating custom Shopify sections has become essential for modern e-commerce businesses that want to offer unique, branded shopping experiences. Whether you're building a theme for clients or customizing your own store, understanding Shopify's section architecture is crucial. This comprehensive guide walks you through everything you need to know about designing, building, and deploying custom sections that customers love.

Shopify custom sections interface showing theme editor
SHOPIFY CUSTOM SECTIONS INTERFACE SHOWING THEME EDITOR


Understanding Shopify Sections Architecture

Shopify sections represent a paradigm shift in theme development. Rather than forcing merchants to understand code, sections provide a visual, intuitive interface for customizing pages. Each section consists of three core components: the Liquid template, the JSON schema, and optional CSS/JavaScript.

Why Sections Matter

The rise of Shopify sections transformed how merchants interact with their themes. Before sections, customizing a store required developer expertise. Now, any merchant can:

  • Add or remove content blocks without touching code
  • Configure colors, fonts, and spacing through visual editors
  • Reorder content elements on the fly
  • A/B test different layouts and designs

This democratization of customization has made themes more valuable and merchants happier. For developers, it means creating components that are both powerful and intuitive.

The Three-Layer Architecture

Every Shopify section consists of three integrated layers:

  1. The Liquid Template - The HTML and dynamic content rendering layer
  2. The JSON Schema - The configuration and customization interface
  3. CSS & JavaScript - Styling and interactivity (optional but recommended)

These layers work together to create sections that are flexible, user-friendly, and visually stunning. Understanding each layer's role is essential before writing any code.


Creating Section Schema Files

The JSON schema is the heart of a Shopify section. It defines what options merchants can configure, how they appear in the Theme Customizer, and what default values to use.

Schema Structure and Organization

A well-organized schema makes sections intuitive. Here's the fundamental structure:

{
  "name": "Custom Section Name",
  "settings": [
    {
      "type": "text",
      "id": "heading_text",
      "label": "Section Heading",
      "default": "Welcome to Our Store"
    }
  ],
  "blocks": [
    {
      "type": "feature_block",
      "name": "Feature",
      "settings": []
    }
  ],
  "presets": [
    {
      "name": "Custom Section",
      "blocks": [
        { "type": "feature_block" }
      ]
    }
  ]
}

Setting Types Explained

Shopify provides numerous setting types, each with specific use cases:

Text and Selection:

  • text - Simple text input
  • textarea - Multi-line text
  • select - Dropdown with predefined options
  • richtext - Rich text editor with formatting

Visual Selections:

  • image_picker - Choose from media library
  • color - Color picker
  • font_picker - Shopify's available fonts

Product and Collection Selections:

  • product - Single product picker
  • collection - Single collection picker
  • product_list - Multiple product selector

Media and URLs:

  • url - URL input field
  • video_url - Video URL input
  • file_picker - File selection

Structural:

  • header - Section divider header
  • paragraph - Display text (read-only)
  • checkbox - Boolean toggle

Building a Practical Schema Example

Let's create a schema for a "Featured Products Grid" section that demonstrates real-world complexity:

{
  "name": "Featured Products Grid",
  "settings": [
    {
      "type": "text",
      "id": "section_heading",
      "label": "Section Heading",
      "default": "Featured Products"
    },
    {
      "type": "select",
      "id": "columns",
      "label": "Number of Columns",
      "options": [
        { "value": "2", "label": "2 Columns" },
        { "value": "3", "label": "3 Columns" },
        { "value": "4", "label": "4 Columns" }
      ],
      "default": "3"
    },
    {
      "type": "color",
      "id": "bg_color",
      "label": "Background Color",
      "default": "#ffffff"
    },
    {
      "type": "range",
      "id": "padding_top",
      "label": "Top Padding (px)",
      "min": 0,
      "max": 100,
      "step": 10,
      "unit": "px",
      "default": 40
    },
    {
      "type": "checkbox",
      "id": "show_price",
      "label": "Show Product Price",
      "default": true
    }
  ],
  "blocks": [
    {
      "type": "product_item",
      "name": "Product",
      "settings": [
        {
          "type": "product",
          "id": "featured_product",
          "label": "Select Product"
        },
        {
          "type": "text",
          "id": "custom_label",
          "label": "Custom Label (optional)",
          "default": ""
        }
      ]
    }
  ],
  "presets": [
    {
      "name": "Featured Products Grid",
      "blocks": [
        { "type": "product_item" },
        { "type": "product_item" },
        { "type": "product_item" }
      ]
    }
  ]
}

This schema demonstrates key principles: hierarchical organization, sensible defaults, and merchant-friendly naming. Notice how we use settings for global section configuration and blocks for repeatable content.


Section Settings and Blocks in Practice

Understanding how to implement settings and blocks in your Liquid template is where theory becomes reality.

Accessing Settings in Your Liquid Template

Once you've defined settings in your JSON schema, accessing them in Liquid is straightforward:

<div class="featured-products-section" style="background-color: {{ section.settings.bg_color }}; padding-top: {{ section.settings.padding_top }}px;">
  <h2 class="section-heading">{{ section.settings.section_heading }}</h2>

  <div class="products-grid products-grid--{{ section.settings.columns }}-columns">
    {% for block in section.blocks %}
      {% if block.type == 'product_item' %}
        <div class="product-card">
          {% assign product = block.settings.featured_product %}

          {% if product %}
            <div class="product-image">
              {% if product.featured_image %}
                <img src="{{ product.featured_image | image_url: width: 300 }}" alt="{{ product.title }}">
              {% endif %}
            </div>

            <div class="product-info">
              <h3 class="product-title">{{ product.title }}</h3>

              {% if block.settings.custom_label != blank %}
                <span class="custom-label">{{ block.settings.custom_label }}</span>
              {% endif %}

              {% if section.settings.show_price %}
                <p class="product-price">{{ product.price | money }}</p>
              {% endif %}

              <a href="{{ product.url }}" class="button">View Product</a>
            </div>
          {% endif %}
        </div>
      {% endif %}
    {% endfor %}
  </div>
</div>

Responsive Design with Section Settings

Modern sections must work across all devices. Use section settings to allow merchants to control responsive behavior:

{
  "type": "select",
  "id": "mobile_columns",
  "label": "Mobile Columns",
  "options": [
    { "value": "1", "label": "1 Column" },
    { "value": "2", "label": "2 Columns" }
  ],
  "default": "1"
}

Then in CSS, use these settings for responsive layouts:

<style>
  @media (max-width: 768px) {
    .products-grid--{{ section.settings.mobile_columns }}-columns {
      grid-template-columns: repeat({{ section.settings.mobile_columns }}, 1fr);
    }
  }
</style>

Block-Level Customization Best Practices

When building blocks, think about what merchants might customize for each instance:

"blocks": [
  {
    "type": "testimonial",
    "name": "Testimonial",
    "settings": [
      {
        "type": "image_picker",
        "id": "customer_image",
        "label": "Customer Photo"
      },
      {
        "type": "text",
        "id": "customer_name",
        "label": "Customer Name",
        "default": "Jane Doe"
      },
      {
        "type": "textarea",
        "id": "testimonial_text",
        "label": "Testimonial Text",
        "default": "This product exceeded my expectations."
      },
      {
        "type": "range",
        "id": "rating",
        "label": "Star Rating",
        "min": 1,
        "max": 5,
        "default": 5
      }
    ]
  }
]

Dynamic Content Integration

The true power of Shopify sections emerges when you integrate dynamic content from your product catalog, collections, and custom data.

Pulling Product Data into Sections

Product-related sections require careful handling of product objects and their nested properties. Here's a robust example:

{% assign product = block.settings.product %}

{% if product %}
  <!-- Product Meta Fields -->
  {% if product.metafields.custom.featured_badge %}
    <span class="badge">{{ product.metafields.custom.featured_badge }}</span>
  {% endif %}

  <!-- Variant Information -->
  {% if product.variants.size > 0 %}
    <select class="variant-selector">
      {% for variant in product.variants %}
        <option value="{{ variant.id }}" {% if variant.available == false %}disabled{% endif %}>
          {{ variant.title }} - {{ variant.price | money }}
          {% if variant.available == false %}(Out of Stock){% endif %}
        </option>
      {% endfor %}
    </select>
  {% endif %}

  <!-- Availability Status -->
  <p class="availability">
    {% if product.available %}
      <span class="in-stock">In Stock</span>
    {% else %}
      <span class="out-of-stock">Out of Stock</span>
    {% endif %}
  </p>
{% endif %}

Collection-Based Dynamic Sections

Some sections pull multiple products from a collection. Use collection pickers in your schema:

{
  "type": "collection",
  "id": "featured_collection",
  "label": "Select Collection"
}

Then iterate through products in your Liquid template:

{% assign collection = section.settings.featured_collection %}

{% if collection %}
  <h2>{{ collection.title }}</h2>

  <div class="products-grid">
    {% for product in collection.products limit: 8 %}
      <div class="product-card">
        <a href="{{ product.url }}">
          <img src="{{ product.featured_image | image_url: width: 300 }}" alt="{{ product.title }}">
          <h3>{{ product.title }}</h3>
          <p class="price">{{ product.price | money }}</p>
        </a>
      </div>
    {% endfor %}
  </div>

  <a href="{{ collection.url }}" class="view-all-link">View All Products</a>
{% endif %}

Using Metafields for Custom Data

Metafields allow you to attach custom data to products, collections, and other resources. Access them in sections like this:

<!-- Product metafield examples -->
{% assign sustainability_info = product.metafields.sustainability.certification %}
{% assign custom_description = product.metafields.custom.short_description %}
{% assign designer_name = product.metafields.brand.designer %}

<div class="product-details">
  {% if sustainability_info %}
    <p>Certification: {{ sustainability_info }}</p>
  {% endif %}

  {% if custom_description %}
    <p>{{ custom_description }}</p>
  {% endif %}

  {% if designer_name %}
    <span>By {{ designer_name }}</span>
  {% endif %}
</div>

Best Practices for Reusable Sections

Creating sections that work across different stores and use cases requires discipline and foresight. Here are proven strategies that separate professional sections from amateur ones.

Section Documentation and Naming

Always include descriptive comments in your schema explaining non-obvious settings:

{
  "type": "select",
  "id": "image_position",
  "label": "Image Position",
  "info": "Determines if the image appears on the left or right side. Text will flow on the opposite side.",
  "options": [
    { "value": "left", "label": "Left" },
    { "value": "right", "label": "Right" }
  ],
  "default": "left"
}

The info field provides helpful context to merchants configuring your section, reducing support burden.

Building Flexible, Context-Agnostic Sections

The best sections work on any page type without custom configuration. Achieve this by:

  1. Avoiding page-specific assumptions - Don't assume you're on a product page or homepage
  2. Providing manual input options - Include text/image pickers alongside auto-filled options
  3. Using sensible defaults - Section should look good immediately after adding it
  4. Making everything optional - Merchants should be able to use your section partially

Example of a flexible section:

{
  "name": "Hero with Optional CTA",
  "settings": [
    {
      "type": "textarea",
      "id": "hero_text",
      "label": "Hero Text",
      "default": "Welcome to our store"
    },
    {
      "type": "image_picker",
      "id": "background_image",
      "label": "Background Image"
    },
    {
      "type": "text",
      "id": "button_text",
      "label": "Button Text (leave blank to hide)",
      "default": "Shop Now"
    },
    {
      "type": "url",
      "id": "button_link",
      "label": "Button Link",
      "default": "/collections/all"
    }
  ]
}

CSS and JavaScript Best Practices

Encapsulate your section's styles to prevent conflicts with other sections:

{% stylesheet %}
  #shopify-section-{{ section.id }} .featured-section {
    background-color: {{ section.settings.bg_color }};
  }

  #shopify-section-{{ section.id }} .featured-section__heading {
    font-size: {{ section.settings.heading_size }}px;
    color: {{ section.settings.heading_color }};
  }
{% endstylesheet %}

Use section.id to namespace your styles, preventing CSS conflicts when the same section appears multiple times on a page.

For JavaScript, similarly namespace your selectors:

{% javascript %}
  document.addEventListener('shopify:section:load', function(e) {
    const sectionId = e.detail.sectionId;
    const section = document.getElementById('shopify-section-' + sectionId);

    // Your JavaScript logic here
    const buttons = section.querySelectorAll('.cta-button');
    buttons.forEach(button => {
      button.addEventListener('click', () => {
        // Handle click
      });
    });
  });
{% endjavascript %}

Performance-First Development

When building sections that will be used thousands of times across different stores, performance matters tremendously.

Liquid Performance Tips:

  • Minimize loops and nested loops
  • Avoid expensive filters like where in loops
  • Use limit to restrict iterations
  • Cache computed values in variables
  • Use break to exit loops early when possible

Image Optimization:

<!-- Optimize images by using appropriate widths and formats -->
<img
  src="{{ image | image_url: width: 400 }}"
  srcset="{{ image | image_url: width: 400 }} 400w,
          {{ image | image_url: width: 800 }} 800w"
  sizes="(max-width: 768px) 100vw, 50vw"
  alt="{{ alt_text }}"
  loading="lazy"
>

Asset Bundling:

<!-- Load CSS only when section is present -->
{{ 'featured-section.css' | asset_url | stylesheet_tag }}

<!-- Load JS only when needed -->
{% if section.settings.enable_slider %}
  {{ 'featured-section-slider.js' | asset_url | script_tag }}
{% endif %}

Advanced Integration: E-commerce Optimization

When you're building sections for Shopify stores, integrating directly with the e-commerce platform creates powerful opportunities.

Leveraging Shopify Analytics

Sections can be enhanced with analytics data to show trending products or top-performing collections:

{% for product in collection.products %}
  {% if product.variants.first.inventory_quantity > 0 %}
    <!-- Show stock levels based on real-time data -->
    <span class="stock-indicator">
      Only {{ product.variants.first.inventory_quantity }} left
    </span>
  {% endif %}
{% endfor %}

Subscription and Payment Integration

Modern sections can integrate subscription products and alternative payment methods:

{
  "type": "checkbox",
  "id": "show_subscription_option",
  "label": "Show Subscription Option",
  "default": false
}

Then use the Shopify API to detect and display subscription-eligible products within your section.

Cart Integration Best Practices

Sections that add items to the cart should follow accessibility and UX standards:

<form method="post" action="/cart/add" class="product-form">
  <input type="hidden" name="id" value="{{ product.variants.first.id }}">

  <select name="quantity" aria-label="Quantity">
    {% for i in (1..10) %}
      <option value="{{ i }}">{{ i }}</option>
    {% endfor %}
  </select>

  <button type="submit" class="button button--primary" aria-label="Add {{ product.title }} to cart">
    Add to Cart
  </button>
</form>

Implementation Strategy and Deployment

Taking custom sections from development to production requires careful planning and testing.

Testing Custom Sections

Before deploying, test comprehensively:

  1. Visual Testing - Check on desktop, tablet, and mobile
  2. Schema Testing - Verify all settings work as expected
  3. Performance Testing - Use Lighthouse to check load times
  4. Edge Case Testing - Test with long product names, missing images, empty states
  5. Browser Testing - Ensure compatibility across modern browsers

Submitting to Shopify Theme Store

If you've built a reusable section, consider submitting it to the Shopify Theme Store. This requires:

  • Clean, well-commented code
  • Comprehensive documentation
  • No hardcoded content
  • Accessibility compliance
  • Performance optimization

Maintaining Sections Post-Launch

Keep sections updated by:

  • Monitoring theme updates and Shopify deprecations
  • Gathering merchant feedback
  • Optimizing based on performance metrics
  • Adding new features based on merchant requests
  • Maintaining backward compatibility when possible

Troubleshooting Common Section Issues

Section Not Appearing in Theme Customizer

Ensure your section file is properly named (section-name.liquid) and located in the sections directory. Verify the JSON schema is valid by checking for syntax errors.

Settings Not Saving

Clear your browser cache and try again. If issues persist, check for console errors and validate your schema against Shopify's schema specification.

Performance Degradation with Multiple Sections

Use browser DevTools to profile. Often, the issue is unnecessary Liquid rendering or unoptimized images. Implement caching where possible and consider moving logic to JavaScript.

Mobile Responsiveness Issues

Test with actual mobile devices, not just browser emulation. Use flexbox and CSS grid for responsive layouts, and test your section at various viewport widths.


Next Steps: Mastering Shopify Development

Custom sections represent just one aspect of Shopify development. To build a complete understanding, explore:

  • Advanced Shopify API integration for custom storefronts
  • Theme app extensions for expanded functionality
  • Liquid templating best practices
  • Performance optimization techniques

Building high-quality custom sections positions you as a skilled Shopify developer and creates immense value for merchants. Start with simple sections, master the fundamentals, and progressively build more complex, feature-rich components.


Key Takeaways

  • Shopify sections democratize theme customization through intuitive visual editors
  • Well-structured JSON schemas are the foundation of usable sections
  • Settings control global section behavior; blocks provide repeatable elements
  • Dynamic content integration transforms static sections into powerful tools
  • Reusable sections require careful planning, documentation, and testing
  • Performance and accessibility should be built in from the start

Want to optimize your entire Shopify store for maximum conversions? Get your free e-commerce audit to identify performance improvements and conversion opportunities, or schedule a consultation with our e-commerce specialists to discuss a custom development strategy.

Ready to Dominate AI Search?

Get your free AI visibility audit and see how your brand appears across ChatGPT, Claude, and more.

Get Your Free Audit