Sections & Blocks

Sections and blocks are the backbone of every Shopify theme. They make themes modular, customizable, and merchant-friendly. This chapter teaches you how to design, build, and configure sections with schemas that empower merchants to create beautiful pages without touching code.

๐Ÿ“Œ Chapter Overview

Time estimate: 90 โ€“ 120 minutes
Tags: Liquid HTML CSS

The Section Concept

Think of a section as a LEGO brick. Each section is a self-contained module that does one thing well. Merchants stack these bricks together through the theme editor to compose complete pages. Some sections are simple (a rich text block), while others are complex (a full product page).

Every section contains up to four parts:

๐Ÿ“

Liquid Markup

The HTML structure rendered by the section. Uses Liquid tags to access settings and dynamic data.

๐ŸŽจ

Styles

CSS for the section. Can be inline via {% style %} or loaded from assets/.

โšก

JavaScript

Interactive behavior. Loaded from assets/ with defer for performance.

โš™๏ธ

Schema

JSON configuration that defines settings, blocks, and presets for the theme editor.

The Schema โ€” Deep Dive

The schema is the most important part of a section for professional theme development. It defines how the section appears and behaves in the theme editor. A well-designed schema makes the difference between a frustrating theme and a delightful one.

Schema Structure

Complete Schema Structure
{% schema %}
{
  "name": "Section Display Name",
  "tag": "section",
  "class": "section",
  "limit": 1,
  "settings": [ ... ],
  "blocks": [ ... ],
  "max_blocks": 16,
  "presets": [ ... ],
  "default": { ... },
  "templates": ["index", "collection", "product"],
  "enabled_on": { ... },
  "disabled_on": { ... }
}
{% endschema %}
Property Required Description
name Yes Display name shown in the theme editor sidebar
tag No HTML tag wrapping the section (default: div)
class No CSS class added to the section wrapper
limit No Maximum times this section can be added to a page
settings No Array of setting input definitions
blocks No Array of block type definitions
max_blocks No Maximum number of blocks allowed (default: 16)
presets No Makes the section available in "Add section" menu
templates No Restrict which page types can use this section
enabled_on No Enable section on specific templates or groups
disabled_on No Disable section on specific templates or groups

All Setting Types

Shopify provides a rich set of setting types. Choosing the right type is crucial for a good merchant experience. Here's every available type:

Basic Input Settings

Type Renders As Returns Use For
text Single-line text input String Headings, labels, short text
textarea Multi-line text area String Paragraphs, descriptions
richtext Rich text editor HTML String Formatted content with bold, italic, links
inline_richtext Inline rich text HTML String Short formatted text (no block elements)
number Number input Number Counts, spacing values
range Slider Number Opacity, font size scale, spacing
checkbox Toggle checkbox Boolean Show/hide elements, enable features
select Dropdown menu String (option value) Predefined choices (layout, alignment)
radio Radio buttons String (option value) Visible choices (2โ€“4 options)

Specialized Settings

Type Renders As Returns Use For
color Color picker Color string Custom colors for section elements
color_scheme Color scheme picker Scheme ID Selecting predefined color schemes
color_background Background color/gradient picker CSS value Backgrounds with gradient support
font_picker Font picker Font object Custom typography
image_picker Image upload / library Image object Background images, banners, logos
video Video upload Video object Background videos, media
video_url Video URL input URL string YouTube/Vimeo embeds
url URL picker URL string Links, CTA buttons
collection Collection picker Collection object Featured collection sections
product Product picker Product object Featured product sections
blog Blog picker Blog object Blog post sections
page Page picker Page object Content from pages
link_list Menu picker Linklist object Navigation menus
html HTML editor HTML string Custom embed code, third-party widgets
liquid Liquid code editor Rendered Liquid Custom Liquid code injection

Layout Settings (Non-Input)

Type Purpose
header Adds a heading label to visually group settings in the editor
paragraph Adds descriptive text between settings for instructions

Setting Examples with All Properties

Comprehensive Setting Examples
"settings": [
  {
    "type": "header",
    "content": "Layout Options"
  },
  {
    "type": "paragraph",
    "content": "Configure how this section appears on the page."
  },
  {
    "type": "text",
    "id": "heading",
    "label": "Heading",
    "default": "Featured Products",
    "info": "Leave blank to hide the heading",
    "placeholder": "Enter a heading..."
  },
  {
    "type": "richtext",
    "id": "description",
    "label": "Description",
    "default": "<p>Discover our curated collection.</p>"
  },
  {
    "type": "select",
    "id": "layout",
    "label": "Layout style",
    "options": [
      { "value": "grid", "label": "Grid" },
      { "value": "carousel", "label": "Carousel" },
      { "value": "list", "label": "List" }
    ],
    "default": "grid",
    "info": "Choose how products are displayed"
  },
  {
    "type": "range",
    "id": "columns",
    "label": "Products per row",
    "min": 2,
    "max": 5,
    "step": 1,
    "default": 4,
    "info": "Desktop only. Mobile shows 2 columns."
  },
  {
    "type": "range",
    "id": "products_to_show",
    "label": "Number of products",
    "min": 2,
    "max": 24,
    "step": 2,
    "default": 8
  },
  {
    "type": "collection",
    "id": "collection",
    "label": "Collection"
  },
  {
    "type": "checkbox",
    "id": "show_vendor",
    "label": "Show product vendor",
    "default": false
  },
  {
    "type": "checkbox",
    "id": "show_secondary_image",
    "label": "Show second image on hover",
    "default": true,
    "info": "Displays the second product image when hovering"
  },
  {
    "type": "header",
    "content": "Colors"
  },
  {
    "type": "color",
    "id": "background_color",
    "label": "Background color",
    "default": "#ffffff"
  },
  {
    "type": "header",
    "content": "Spacing"
  },
  {
    "type": "range",
    "id": "padding_top",
    "label": "Top padding",
    "min": 0,
    "max": 100,
    "step": 4,
    "default": 36,
    "unit": "px"
  },
  {
    "type": "range",
    "id": "padding_bottom",
    "label": "Bottom padding",
    "min": 0,
    "max": 100,
    "step": 4,
    "default": 36,
    "unit": "px"
  }
]
โœ… Schema Design Best Practices

1. Use header to group related settings โ€” helps merchants find what they need.
2. Add info text to explain non-obvious settings โ€” reduce support requests.
3. Always provide default values โ€” the section should look great out of the box.
4. Use range instead of number when there are min/max constraints โ€” prevents invalid input.
5. Include padding controls โ€” merchants need spacing flexibility between sections.

Blocks โ€” Sub-Components

Blocks are repeatable, reorderable sub-components within a section. They're what make sections truly flexible. Merchants can add multiple blocks, reorder them, and remove them โ€” all through the theme editor.

When to Use Blocks

Use blocks when a section needs to contain a variable number of similar or mixed content items:

  • Product page: title block, price block, variant picker block, description block, share buttons block
  • Testimonials section: multiple testimonial blocks
  • Feature list: multiple feature blocks with icon + text
  • Image gallery: multiple image blocks
  • FAQ section: multiple question/answer blocks
  • Multi-column: multiple column blocks with different content types

Block Schema Definition

Block Schema Example โ€” Testimonials
"blocks": [
  {
    "type": "testimonial",
    "name": "Testimonial",
    "limit": 6,
    "settings": [
      {
        "type": "image_picker",
        "id": "avatar",
        "label": "Customer photo"
      },
      {
        "type": "text",
        "id": "author",
        "label": "Customer name",
        "default": "Jane Smith"
      },
      {
        "type": "textarea",
        "id": "quote",
        "label": "Testimonial text",
        "default": "This product changed my life. Highly recommended!"
      },
      {
        "type": "range",
        "id": "rating",
        "label": "Star rating",
        "min": 1,
        "max": 5,
        "step": 1,
        "default": 5
      }
    ]
  }
]

Rendering Blocks in Liquid

Rendering Blocks
<div class="testimonials__grid">
  {%- for block in section.blocks -%}
    <div class="testimonial-card" {{ block.shopify_attributes }}>

      {%- if block.settings.avatar -%}
        <img
          class="testimonial-card__avatar"
          src="{{ block.settings.avatar | image_url: width: 80 }}"
          alt="{{ block.settings.author | escape }}"
          width="80"
          height="80"
          loading="lazy"
        >
      {%- endif -%}

      <blockquote class="testimonial-card__quote">
        {{ block.settings.quote }}
      </blockquote>

      <div class="testimonial-card__stars">
        {%- for i in (1..block.settings.rating) -%}
          โ˜…
        {%- endfor -%}
        {%- assign empty_stars = 5 | minus: block.settings.rating -%}
        {%- for i in (1..empty_stars) -%}
          โ˜†
        {%- endfor -%}
      </div>

      <cite class="testimonial-card__author">
        {{ block.settings.author }}
      </cite>

    </div>
  {%- endfor -%}
</div>
๐Ÿšจ Always Include block.shopify_attributes

The {{ block.shopify_attributes }} output is required on the block's wrapper element. It provides data attributes that the theme editor uses to identify, select, and interact with individual blocks. Without it, merchants can't click on blocks to edit them in the theme editor.

Mixed Block Types

Sections can define multiple block types. This lets merchants mix different content within a section. Use {% case block.type %} to render each type differently:

Mixed Block Types โ€” Multi-Column Section
{%- comment -%} LIQUID MARKUP {%- endcomment -%}
<section class="multi-content section-{{ section.id }}">
  <div class="page-width">
    {%- if section.settings.heading != blank -%}
      <h2 class="multi-content__heading">{{ section.settings.heading }}</h2>
    {%- endif -%}

    <div class="multi-content__grid">
      {%- for block in section.blocks -%}
        <div class="multi-content__item" {{ block.shopify_attributes }}>
          {%- case block.type -%}

            {%- when 'text_block' -%}
              <div class="content-text">
                {%- if block.settings.title != blank -%}
                  <h3>{{ block.settings.title }}</h3>
                {%- endif -%}
                {{ block.settings.body }}
              </div>

            {%- when 'image_block' -%}
              <div class="content-image">
                {%- if block.settings.image -%}
                  <img
                    src="{{ block.settings.image | image_url: width: 600 }}"
                    alt="{{ block.settings.image.alt | escape }}"
                    loading="lazy"
                  >
                {%- endif -%}
                {%- if block.settings.caption != blank -%}
                  <p class="content-image__caption">{{ block.settings.caption }}</p>
                {%- endif -%}
              </div>

            {%- when 'video_block' -%}
              <div class="content-video">
                {%- if block.settings.video_url != blank -%}
                  {{ block.settings.video_url | external_video_tag }}
                {%- endif -%}
              </div>

            {%- when 'cta_block' -%}
              <div class="content-cta">
                <h3>{{ block.settings.heading }}</h3>
                <p>{{ block.settings.text }}</p>
                {%- if block.settings.button_label != blank -%}
                  <a href="{{ block.settings.button_link }}" class="btn">
                    {{ block.settings.button_label }}
                  </a>
                {%- endif -%}
              </div>

          {%- endcase -%}
        </div>
      {%- endfor -%}
    </div>
  </div>
</section>

{%- comment -%} SCHEMA {%- endcomment -%}
{% schema %}
{
  "name": "Multi-Content",
  "settings": [
    {
      "type": "text",
      "id": "heading",
      "label": "Heading",
      "default": "Why Choose Us"
    }
  ],
  "blocks": [
    {
      "type": "text_block",
      "name": "Text",
      "settings": [
        {
          "type": "text",
          "id": "title",
          "label": "Title"
        },
        {
          "type": "richtext",
          "id": "body",
          "label": "Body text"
        }
      ]
    },
    {
      "type": "image_block",
      "name": "Image",
      "settings": [
        {
          "type": "image_picker",
          "id": "image",
          "label": "Image"
        },
        {
          "type": "text",
          "id": "caption",
          "label": "Caption"
        }
      ]
    },
    {
      "type": "video_block",
      "name": "Video",
      "settings": [
        {
          "type": "video_url",
          "id": "video_url",
          "label": "Video URL",
          "accept": ["youtube", "vimeo"]
        }
      ]
    },
    {
      "type": "cta_block",
      "name": "Call to Action",
      "limit": 2,
      "settings": [
        {
          "type": "text",
          "id": "heading",
          "label": "Heading"
        },
        {
          "type": "text",
          "id": "text",
          "label": "Text"
        },
        {
          "type": "text",
          "id": "button_label",
          "label": "Button label"
        },
        {
          "type": "url",
          "id": "button_link",
          "label": "Button link"
        }
      ]
    }
  ],
  "presets": [
    {
      "name": "Multi-Content",
      "blocks": [
        { "type": "text_block" },
        { "type": "image_block" },
        { "type": "text_block" }
      ]
    }
  ]
}
{% endschema %}

CSS Strategies for Sections

There are two main approaches for styling sections:

Approach 1: External CSS File (Recommended)

Section with External CSS
<!-- Load the CSS file at the top of the section -->
{{ 'section-testimonials.css' | asset_url | stylesheet_tag }}

<section class="testimonials">
  <!-- section content -->
</section>

Approach 2: Inline Styles with Dynamic Values

Use the {% style %} tag for CSS that depends on section settings (like custom colors, padding, or fonts):

Dynamic Inline Styles
{%- style -%}
  .section-{{ section.id }} {
    padding-top: {{ section.settings.padding_top }}px;
    padding-bottom: {{ section.settings.padding_bottom }}px;
    background-color: {{ section.settings.background_color }};
  }

  .section-{{ section.id }} .heading {
    color: {{ section.settings.heading_color }};
    text-align: {{ section.settings.text_alignment }};
  }

  @media (max-width: 749px) {
    .section-{{ section.id }} {
      padding-top: {{ section.settings.padding_top | divided_by: 2 }}px;
      padding-bottom: {{ section.settings.padding_bottom | divided_by: 2 }}px;
    }
  }
{%- endstyle -%}

<section class="testimonials section-{{ section.id }}">
  <h2 class="heading">{{ section.settings.heading }}</h2>
  <!-- rest of content -->
</section>
๐Ÿ’ก Combine Both Approaches

Professional themes use both: external CSS for base styles and layout, inline {% style %} for dynamic values from settings. This gives you the best of both worlds โ€” cacheable base CSS plus customizable properties.

Presets vs. Defaults

Understanding the difference between "presets" and "default" prevents a common confusion:

Feature presets default
Purpose Makes section available in "Add section" menu Defines initial state when section is in a template
When used When merchant manually adds the section When section is referenced in a JSON template
Multiple allowed โœ“ Yes (creates multiple presets) โœ— Only one
Can include blocks โœ“ Yes โœ“ Yes
Multiple Presets Example
"presets": [
  {
    "name": "Testimonials โ€” Grid",
    "settings": {
      "layout": "grid",
      "columns": 3
    },
    "blocks": [
      { "type": "testimonial" },
      { "type": "testimonial" },
      { "type": "testimonial" }
    ]
  },
  {
    "name": "Testimonials โ€” Carousel",
    "settings": {
      "layout": "carousel",
      "auto_rotate": true
    },
    "blocks": [
      { "type": "testimonial" },
      { "type": "testimonial" },
      { "type": "testimonial" },
      { "type": "testimonial" }
    ]
  }
]

App Blocks

Online Store 2.0 introduced app blocks โ€” the ability for third-party apps to inject content into your sections. To support this, add the special @app block type:

Supporting App Blocks
"blocks": [
  {
    "type": "title",
    "name": "Title",
    "limit": 1,
    "settings": []
  },
  {
    "type": "price",
    "name": "Price",
    "limit": 1,
    "settings": []
  },
  {
    "type": "@app"
  }
]

In your Liquid, render app blocks alongside your own blocks:

Rendering App Blocks
{%- for block in section.blocks -%}
  {%- case block.type -%}
    {%- when 'title' -%}
      <h1 {{ block.shopify_attributes }}>{{ product.title }}</h1>

    {%- when 'price' -%}
      <div {{ block.shopify_attributes }}>{{ product.price | money }}</div>

    {%- when '@app' -%}
      <div {{ block.shopify_attributes }}>
        {% render block %}
      </div>
  {%- endcase -%}
{%- endfor -%}
โœ… Always Support App Blocks

Adding @app block support is a requirement for the Shopify Theme Store and a mark of a professional theme. It lets merchants add reviews, upsells, trust badges, and other app content exactly where they want it โ€” without workarounds.

Exercise: Build a Testimonials Section

๐Ÿงช
Hands-On Exercise

Build a Customer Testimonials Section

โฑ๏ธ 30 minutes ๐Ÿ“ sections/testimonials.liquid ๐Ÿ“ assets/section-testimonials.css

Learning objective: Practice building a complete section with blocks, schema settings, and CSS.

Requirements:

  1. Create sections/testimonials.liquid
  2. Section settings: heading, background color, padding top/bottom
  3. Block type: testimonial (with author name, quote text, star rating, avatar image)
  4. Maximum 6 blocks
  5. Include a preset with 3 default testimonials
  6. Create assets/section-testimonials.css with grid layout
  7. Make it responsive: 3 columns on desktop, 1 column on mobile

Complete code:

sections/testimonials.liquid
{{ 'section-testimonials.css' | asset_url | stylesheet_tag }}

{%- style -%}
  .section-{{ section.id }} {
    padding-top: {{ section.settings.padding_top }}px;
    padding-bottom: {{ section.settings.padding_bottom }}px;
    background-color: {{ section.settings.bg_color }};
  }
{%- endstyle -%}

<section class="testimonials section-{{ section.id }}">
  <div class="page-width">
    {%- if section.settings.heading != blank -%}
      <h2 class="testimonials__heading">
        {{ section.settings.heading | escape }}
      </h2>
    {%- endif -%}

    {%- if section.blocks.size > 0 -%}
      <div class="testimonials__grid">
        {%- for block in section.blocks -%}
          <div class="testimonial-card" {{ block.shopify_attributes }}>
            <div class="testimonial-card__stars">
              {%- for i in (1..5) -%}
                {%- if i <= block.settings.rating -%}
                  <span class="star star--filled">โ˜…</span>
                {%- else -%}
                  <span class="star star--empty">โ˜†</span>
                {%- endif -%}
              {%- endfor -%}
            </div>

            <blockquote class="testimonial-card__quote">
              "{{ block.settings.quote | escape }}"
            </blockquote>

            <div class="testimonial-card__author">
              {%- if block.settings.avatar -%}
                <img
                  src="{{ block.settings.avatar | image_url: width: 60, height: 60, crop: 'center' }}"
                  alt="{{ block.settings.author | escape }}"
                  class="testimonial-card__avatar"
                  width="60"
                  height="60"
                  loading="lazy"
                >
              {%- endif -%}
              <span class="testimonial-card__name">
                {{ block.settings.author | escape }}
              </span>
            </div>
          </div>
        {%- endfor -%}
      </div>
    {%- endif -%}
  </div>
</section>

{% schema %}
{
  "name": "Testimonials",
  "tag": "section",
  "class": "section",
  "settings": [
    {
      "type": "text",
      "id": "heading",
      "label": "Heading",
      "default": "What Our Customers Say"
    },
    {
      "type": "color",
      "id": "bg_color",
      "label": "Background color",
      "default": "#f8f9fb"
    },
    {
      "type": "range",
      "id": "padding_top",
      "label": "Top padding",
      "min": 0,
      "max": 100,
      "step": 4,
      "default": 40,
      "unit": "px"
    },
    {
      "type": "range",
      "id": "padding_bottom",
      "label": "Bottom padding",
      "min": 0,
      "max": 100,
      "step": 4,
      "default": 40,
      "unit": "px"
    }
  ],
  "blocks": [
    {
      "type": "testimonial",
      "name": "Testimonial",
      "settings": [
        {
          "type": "image_picker",
          "id": "avatar",
          "label": "Customer photo"
        },
        {
          "type": "text",
          "id": "author",
          "label": "Customer name",
          "default": "Happy Customer"
        },
        {
          "type": "textarea",
          "id": "quote",
          "label": "Testimonial",
          "default": "Amazing product quality and fast shipping. Will definitely order again!"
        },
        {
          "type": "range",
          "id": "rating",
          "label": "Rating",
          "min": 1,
          "max": 5,
          "step": 1,
          "default": 5
        }
      ]
    }
  ],
  "max_blocks": 6,
  "presets": [
    {
      "name": "Testimonials",
      "blocks": [
        {
          "type": "testimonial",
          "settings": {
            "author": "Sarah M.",
            "quote": "Best purchase I've made this year. The quality is outstanding!",
            "rating": 5
          }
        },
        {
          "type": "testimonial",
          "settings": {
            "author": "James K.",
            "quote": "Fast shipping and exactly as described. Highly recommend.",
            "rating": 5
          }
        },
        {
          "type": "testimonial",
          "settings": {
            "author": "Emily R.",
            "quote": "Beautiful design and great customer service. Love it!",
            "rating": 4
          }
        }
      ]
    }
  ]
}
{% endschema %}
assets/section-testimonials.css
.testimonials__heading {
  text-align: center;
  margin-bottom: 2rem;
  font-size: 1.75rem;
}

.testimonials__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.5rem;
}

.testimonial-card {
  background: #fff;
  border-radius: 12px;
  padding: 2rem;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.testimonial-card__stars {
  display: flex;
  gap: 2px;
}

.star--filled {
  color: #f59e0b;
}

.star--empty {
  color: #d1d5db;
}

.testimonial-card__quote {
  font-style: italic;
  line-height: 1.7;
  color: #4a5568;
  flex: 1;
  margin: 0;
  border: none;
  padding: 0;
}

.testimonial-card__author {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-top: auto;
}

.testimonial-card__avatar {
  border-radius: 50%;
  object-fit: cover;
}

.testimonial-card__name {
  font-weight: 600;
  font-size: 0.9rem;
}

@media (max-width: 999px) {
  .testimonials__grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 599px) {
  .testimonials__grid {
    grid-template-columns: 1fr;
  }
}

Exercise: Build a Features Section with Mixed Blocks

๐Ÿงช
Hands-On Exercise

Build a Features Section with Icons

โฑ๏ธ 25 minutes ๐Ÿ“ sections/features.liquid

Learning objective: Create a section with a single block type that merchants can add multiple times, each with an icon, heading, and description.

Requirements:

  1. Section settings: heading, subheading, columns count (2โ€“4), padding
  2. Block type: feature (icon emoji, title, description)
  3. Responsive grid layout
  4. Preset with 3 features (Free Shipping, Secure Payment, 24/7 Support)
sections/features.liquid
{%- style -%}
  .section-{{ section.id }} {
    padding-top: {{ section.settings.padding_top }}px;
    padding-bottom: {{ section.settings.padding_bottom }}px;
  }
  .section-{{ section.id }} .features__grid {
    grid-template-columns: repeat({{ section.settings.columns }}, 1fr);
  }
  @media (max-width: 749px) {
    .section-{{ section.id }} .features__grid {
      grid-template-columns: 1fr;
    }
  }
{%- endstyle -%}

<section class="features section-{{ section.id }}">
  <div class="page-width">
    {%- if section.settings.heading != blank -%}
      <div class="features__header">
        <h2>{{ section.settings.heading | escape }}</h2>
        {%- if section.settings.subheading != blank -%}
          <p class="features__subheading">
            {{ section.settings.subheading | escape }}
          </p>
        {%- endif -%}
      </div>
    {%- endif -%}

    {%- if section.blocks.size > 0 -%}
      <div class="features__grid">
        {%- for block in section.blocks -%}
          <div class="feature-item" {{ block.shopify_attributes }}>
            {%- if block.settings.icon != blank -%}
              <div class="feature-item__icon">
                {{ block.settings.icon }}
              </div>
            {%- endif -%}
            {%- if block.settings.title != blank -%}
              <h3 class="feature-item__title">
                {{ block.settings.title | escape }}
              </h3>
            {%- endif -%}
            {%- if block.settings.description != blank -%}
              <p class="feature-item__desc">
                {{ block.settings.description | escape }}
              </p>
            {%- endif -%}
          </div>
        {%- endfor -%}
      </div>
    {%- endif -%}
  </div>
</section>

{% schema %}
{
  "name": "Features",
  "settings": [
    {
      "type": "text",
      "id": "heading",
      "label": "Heading",
      "default": "Why Shop With Us"
    },
    {
      "type": "text",
      "id": "subheading",
      "label": "Subheading",
      "default": "We're committed to giving you the best experience"
    },
    {
      "type": "range",
      "id": "columns",
      "label": "Columns",
      "min": 2,
      "max": 4,
      "step": 1,
      "default": 3
    },
    {
      "type": "range",
      "id": "padding_top",
      "label": "Top padding",
      "min": 0, "max": 100, "step": 4, "default": 40, "unit": "px"
    },
    {
      "type": "range",
      "id": "padding_bottom",
      "label": "Bottom padding",
      "min": 0, "max": 100, "step": 4, "default": 40, "unit": "px"
    }
  ],
  "blocks": [
    {
      "type": "feature",
      "name": "Feature",
      "settings": [
        {
          "type": "text",
          "id": "icon",
          "label": "Icon (emoji)",
          "default": "๐Ÿš€",
          "info": "Paste any emoji character"
        },
        {
          "type": "text",
          "id": "title",
          "label": "Title",
          "default": "Feature Title"
        },
        {
          "type": "textarea",
          "id": "description",
          "label": "Description",
          "default": "Describe this feature and why customers will love it."
        }
      ]
    }
  ],
  "max_blocks": 8,
  "presets": [
    {
      "name": "Features",
      "blocks": [
        {
          "type": "feature",
          "settings": {
            "icon": "๐Ÿšš",
            "title": "Free Shipping",
            "description": "Free shipping on all orders over $50. No hidden fees."
          }
        },
        {
          "type": "feature",
          "settings": {
            "icon": "๐Ÿ”’",
            "title": "Secure Payment",
            "description": "Your payment information is processed securely."
          }
        },
        {
          "type": "feature",
          "settings": {
            "icon": "๐Ÿ’ฌ",
            "title": "24/7 Support",
            "description": "Our support team is here to help you anytime."
          }
        }
      ]
    }
  ]
}
{% endschema %}

Section Restrictions

You can control where sections appear using enabled_on and disabled_on:

Restricting Section Availability
{%- comment -%} Only available on product and collection pages {%- endcomment -%}
"enabled_on": {
  "templates": ["product", "collection"]
}

{%- comment -%} Available everywhere EXCEPT the cart page {%- endcomment -%}
"disabled_on": {
  "templates": ["cart"]
}

{%- comment -%} Only in header and footer groups {%- endcomment -%}
"enabled_on": {
  "groups": ["header", "footer"]
}

{%- comment -%} Disable in all section groups (only in templates) {%- endcomment -%}
"disabled_on": {
  "groups": ["*"]
}

Key Takeaways

  • Sections are self-contained modules with markup, CSS, JS, and schema
  • The schema defines settings, blocks, and presets for the theme editor
  • Choose the right setting type for each piece of data (text, range, select, color, etc.)
  • Use header and paragraph to organize settings in the editor
  • Always provide sensible default values
  • Blocks are repeatable, reorderable sub-components within sections
  • Always include {{ block.shopify_attributes }} on block wrappers
  • Support app blocks with "type": "@app"
  • Combine external CSS files with inline {% style %} for dynamic values
  • Use presets to make sections available in the "Add section" menu
  • Use enabled_on / disabled_on to control where sections appear