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.
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
{% 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
"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"
}
]
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
"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
<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>
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:
{%- 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)
<!-- 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):
{%- 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>
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 |
"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:
"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:
{%- 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 -%}
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
Build a Customer Testimonials Section
Learning objective: Practice building a complete section with blocks, schema settings, and CSS.
Requirements:
- Create
sections/testimonials.liquid - Section settings: heading, background color, padding top/bottom
- Block type: testimonial (with author name, quote text, star rating, avatar image)
- Maximum 6 blocks
- Include a preset with 3 default testimonials
- Create
assets/section-testimonials.csswith grid layout - Make it responsive: 3 columns on desktop, 1 column on mobile
Complete code:
{{ '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 %}
.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
Build a Features Section with Icons
Learning objective: Create a section with a single block type that merchants can add multiple times, each with an icon, heading, and description.
Requirements:
- Section settings: heading, subheading, columns count (2โ4), padding
- Block type: feature (icon emoji, title, description)
- Responsive grid layout
- Preset with 3 features (Free Shipping, Secure Payment, 24/7 Support)
{%- 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:
{%- 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
headerandparagraphto 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_onto control where sections appear