Shopify Online Store 2.0: Create dynamic sections with JSON schema and theme editor

introduction

Shopify Online Store 2.0 has fundamentally modernized theme development through modular sections. With JSON Schema, you can release a variety of settings in the theme editor so that store owners control content without code adjustments. In this article, we'll first build a dynamic section for a promotional banner and then show how the approach can be extended to specialized areas of your store.

Part 1: Build a Dynamic Promotion Banner Section

1. Overview

The “Dynamic Promotion Banner” is a flexible, visually strong section that retailers can use at various points in the shop. The section contains a higher-level headline as a header, a promotional headline, subtext, a background image and a call-to-action button. All elements can be configured via JSON schema in the theme editor, so that the section can be edited completely without code.

2. Create section file

Create a new file in the sections directory, for example dynamic-promotion-banner.liquid. In it, you combine liquid markup with JSON schema to define both rendering logic and editor settings.

Liquid markup with a clear header

HTML:

<!-- dynamic-promotion-banner.liquid -->
<section class="dynamic-promotion-banner" style="background-image: url({{ section.settings.banner_image | img_url: '1600x' }});">
  <div class="banner-content" style="text-align: center; padding: 60px 20px;">
    <!-- Header Section -->
    {% if section.settings.header_title != blank %}
      <header>
        <h1 style="color: {{ section.settings.header_color }};">{{ section.settings.header_title }}</h1>
      </header>
    {% endif %}
    <!-- Promotional Headline -->
    {% if section.settings.headline != blank %}
      <h2 style="color: {{ section.settings.headline_color }};">{{ section.settings.headline }}</h2>
    {% endif %}
    <!-- Subtext -->
    {% if section.settings.subtext != blank %}
      <p style="color: {{ section.settings.subtext_color }};">{{ section.settings.subtext }}</p>
    {% endif %}
    <!-- Call-to-Action Button -->
    {% if section.settings.cta_text != blank and section.settings.cta_link != blank %}
      <a href="{{ section.settings.cta_link }}" class="btn" style="background-color: {{ section.settings.cta_bg_color }}; color: {{ section.settings.cta_text_color }};">
        {{ section.settings.cta_text }}
      </a>
    {% endif %}
  </div>
</section>

In this example, we have our own header area with a <h1> added as a primary title. It is separate from the promotional headline and ensures a clear structure.

3. Define JSON schema

The JSON scheme at the end of the file makes all options in the theme editor editable. Here is an example that makes header, headline, subtext, background image and CTA available in the settings.

JSON:

{% schema %}
{
  "name": "Dynamic Promotion Banner",
  "settings": [
    {
      "type": "image_picker",
      "id": "banner_image",
      "label": "Banner Background Image"
    },
    {
      "type": "text",
      "id": "header_title",
      "label": "Header Title",
      "default": "Our Special Announcement"
    },
    {
      "type": "color",
      "id": "header_color",
      "label": "Header Color",
      "default": "#333333"
    },
    {
      "type": "text",
      "id": "headline",
      "label": "Promotional Headline",
      "default": "Welcome to Our Special Offer!"
    },
    {
      "type": "color",
      "id": "headline_color",
      "label": "Headline Color",
      "default": "#ffffff"
    },
    {
      "type": "textarea",
      "id": "subtext",
      "label": "Subtext/Description",
      "default": "Enjoy our exclusive deals and offers, tailored just for you."
    },
    {
      "type": "color",
      "id": "subtext_color",
      "label": "Subtext Color",
      "default": "#ffffff"
    },
    {
      "type": "text",
      "id": "cta_text",
      "label": "CTA Button Text",
      "default": "Shop Now"
    },
    {
      "type": "url",
      "id": "cta_link",
      "label": "CTA Button Link",
      "default": "/collections/all"
    },
    {
      "type": "color",
      "id": "cta_bg_color",
      "label": "CTA Button Background Color",
      "default": "#ff0000"
    },
    {
      "type": "color",
      "id": "cta_text_color",
      "label": "CTA Button Text Color",
      "default": "#ffffff"
    }
  ],
  "presets": [
    {
      "name": "Dynamic Promotion Banner",
      "category": "Custom Sections"
    }
  ]
}
{% endschema %}

This scheme gives merchant teams full control over the banner's content and visuals. After saving, the section appears in the theme editor and can be adjusted there in real time.

4. Integrate section

  • Add a section to a template: Add the section to index.json on the start page, for example.

JSON:

{
  "sections": {
    "banner": {
      "type": "dynamic-promotion-banner"
    }
  },
  "order": ["banner"]
}
  • Customize in the theme editor: “Dynamic Promotion Banner” appears under Online Store > Themes > Customize. All settings, including headers, can be configured live there.

Part 2: Extend approach to specific areas

The principles of the promotional banner can be transferred to specialized sections, for example for a product-related promo section or a card section in the grid. It is crucial to tailor markup and JSON schema to the respective context.

1. Custom section for a single product page

For product-related promos, you can only display additional details, an individual message, or a context-specific CTA on the product page.

Contextual liquid markup with conditional rendering

Liquid:

<!-- product-custom-promo.liquid -->
{% if product.available %}
  <section class="product-custom-promo" style="background: {{ section.settings.background_color }};">
    <div class="promo-content" style="padding: 40px;">
      {% if section.settings.product_header != blank %}
        <header>
          <h1 style="color: {{ section.settings.product_header_color }};">{{ section.settings.product_header }}</h1>
        </header>
      {% endif %}
      <p style="color: {{ section.settings.product_message_color }};">{{ section.settings.product_message }}</p>
      {% if section.settings.cta_text != blank and section.settings.cta_link != blank %}
        <a href="{{ section.settings.cta_link }}" class="btn" style="background-color: {{ section.settings.cta_bg_color }}; color: {{ section.settings.cta_text_color }};">
          {{ section.settings.cta_text }}
        </a>
      {% endif %}
    </div>
  </section>
{% endif %}


JSON schema for the product-related section

JSON:

{% schema %}
{
  "name": "Product Custom Promotion",
  "settings": [
    {
      "type": "color",
      "id": "background_color",
      "label": "Background Color",
      "default": "#f9f9f9"
    },
    {
      "type": "text",
      "id": "product_header",
      "label": "Product Header Title",
      "default": "Special Offer for This Product"
    },
    {
      "type": "color",
      "id": "product_header_color",
      "label": "Product Header Color",
      "default": "#333333"
    },
    {
      "type": "textarea",
      "id": "product_message",
      "label": "Promotional Message",
      "default": "Get an exclusive discount when you buy this product today!"
    },
    {
      "type": "color",
      "id": "product_message_color",
      "label": "Message Text Color",
      "default": "#333333"
    },
    {
      "type": "text",
      "id": "cta_text",
      "label": "CTA Button Text",
      "default": "Buy Now"
    },
    {
      "type": "url",
      "id": "cta_link",
      "label": "CTA Button Link",
      "default": "/cart"
    },
    {
      "type": "color",
      "id": "cta_bg_color",
      "label": "CTA Button Background Color",
      "default": "#ff6600"
    },
    {
      "type": "color",
      "id": "cta_text_color",
      "label": "CTA Button Text Color",
      "default": "#ffffff"
    }
  ],
  "presets": [
    {
      "name": "Product Custom Promotion",
      "category": "Product Sections"
    }
  ]
}
{% endschema %}

This version is tailored to the product context. The settings focus on the product-related message and CTA. Include the section in the product template so that it appears next to the standard product details.

2. Custom card within a card section

If you want to show multiple cards such as featured products, testimonials, or promo content in a grid within a card section, use blocks in the JSON schema and iterate over them with Liquid.

Key adjustments for a card section

  • Loop over cards: With {% for block in section.blocks%} issue multiple cards.
  • Modular settings: Define image, title, text, link and colors per card.
  • Blocks in the scheme: Add a flexible number of cards via the theme editor.

Liquid outline for a card section

Liquid:

<!-- card-section.liquid -->
<section class="custom-card-section">
  <div class="cards-wrapper">
    {% for block in section.blocks %}
      <div class="card" style="border-color: {{ block.settings.border_color }};">
        {% if block.settings.card_image != blank %}
          <img src="{{ block.settings.card_image | img_url: '500x' }}" alt="{{ block.settings.card_title }}">
        {% endif %}
        {% if block.settings.card_title != blank %}
          <h3 style="color: {{ block.settings.title_color }};">{{ block.settings.card_title }}</h3>
        {% endif %}
        <p style="color: {{ block.settings.text_color }};">{{ block.settings.card_text }}</p>
        {% if block.settings.card_link != blank %}
          <a href="{{ block.settings.card_link }}" class="btn">Learn More</a>
        {% endif %}
      </div>
    {% endfor %}
  </div>
</section>

JSON scheme with blocks:

JSON:

{% schema %}
{
  "name": "Custom Card Section",
  "max_blocks": 5,
  "blocks": [
    {
      "type": "card",
      "name": "Card",
      "settings": [
        {
          "type": "image_picker",
          "id": "card_image",
          "label": "Card Image"
        },
        {
          "type": "text",
          "id": "card_title",
          "label": "Card Title",
          "default": "Card Title"
        },
        {
          "type": "textarea",
          "id": "card_text",
          "label": "Card Text",
          "default": "Some descriptive text for the card."
        },
        {
          "type": "url",
          "id": "card_link",
          "label": "Card Link",
          "default": "/"
        },
        {
          "type": "color",
          "id": "border_color",
          "label": "Border Color",
          "default": "#cccccc"
        },
        {
          "type": "color",
          "id": "title_color",
          "label": "Title Color",
          "default": "#333333"
        },
        {
          "type": "color",
          "id": "text_color",
          "label": "Text Color",
          "default": "#333333"
        }
      ]
    }
  ],
  "presets": [
    {
      "name": "Custom Card Section",
      "category": "Custom Sections"
    }
  ]
}
{% endschema %}

With blocks, merchant teams can flexibly add multiple cards in the theme editor and fill them individually. Each card is a block with its own settings.

About UNHYDE®

UNHYDE is a web agency from Munich with a focus on web development, user experience and digital marketing. Our mission is to build powerful digital platforms that create tangible customer experiences and measurable growth. We work internationally and are a recognized Shopify partner agency with numerous successfully launched websites and web stores.

contact

We help you implement modular Shopify sections, structure your JSON schemas, and integrate cleanly into the theme editor. Feel free to write to us hello@unhyde.me for an individual concept.

Shopify Metafields: Implement dynamic product data with Liquid & JSON
Shopify MetaFields & MetaObjects: Program dynamic model data and partner profiles without JSON
Shopify store slow? How to sustainably optimize page speed, performance and load times
Optimize Shopify Performance: How Load Time & Core Web Vitals directly increase your sales
Shopify SEO with E-E-A-T: How to rank on Google in the long term with Experience, Expertise, Authoritativeness & Trust
Shopify & GEO: How to optimize your online shop for AI search engines and generative systems
UNHYDE•UNHYDE•UNHYDE•UNHYDE•UNHYDE•UNHYDE•