Block Schema
Content blocks are reusable components that editors can add to pages, events, and other content types. Each block has a schema that defines its configuration and available fields.
Block Structure
Section titled “Block Structure”A block file consists of two parts:
- Liquid template - The HTML/Liquid code that renders the block
- Schema tag - JSON configuration defining the block’s metadata and settings
{# blocks/hero.liquid #}
<section id="{{ block.id }}" class="hero-block"> <h1>{{ block.heading }}</h1> <p>{{ block.subheading }}</p></section>
{% schema %}{ "name": "hero", "singular": "Hero", "plural": "Heroes", "label": "Hero Block", "settings": [ { "type": "text", "name": "heading", "label": "Heading" }, { "type": "text", "name": "subheading", "label": "Subheading" } ]}{% endschema %}Schema Properties
Section titled “Schema Properties”| Property | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Unique identifier for the block (used in templates) |
singular | string | No | Singular display name |
plural | string | No | Plural display name |
label | string | No | Human-readable label shown in admin |
settings | array | Yes | Array of field configurations |
The block Object
Section titled “The block Object”Inside block templates, the block object provides access to:
| Property | Description |
|---|---|
block.id | Unique identifier for this block instance |
block.type | The block type name (e.g., "hero") |
block.[fieldname] | Value of any field defined in settings |
block.[fieldname]_html | HTML-rendered version of richtext fields |
<section id="{{ block.id }}" class="block-{{ block.type }}"> <h2>{{ block.title }}</h2> {{ block.content_html }}</section>Settings Field Types
Section titled “Settings Field Types”Block settings support all standard field types. See Input Settings for complete documentation.
Text Fields
Section titled “Text Fields”{ "type": "text", "name": "title", "label": "Title"}<h2>{{ block.title }}</h2>Rich Text Fields
Section titled “Rich Text Fields”{ "type": "richtext", "name": "content", "label": "Content"}<div class="prose"> {{ block.content_html }}</div>Checkbox Fields
Section titled “Checkbox Fields”{ "type": "checkbox", "name": "show_border", "label": "Show Border"}<div class="card {% if block.show_border %}card--bordered{% endif %}"> ...</div>Upload (Media) Fields
Section titled “Upload (Media) Fields”{ "type": "upload", "name": "image", "label": "Image", "relationTo": "media"}{% if block.image %} <img src="{{ block.image | image_url: width: 800 }}" alt="{{ block.image.alt }}" >{% endif %}Relationship Fields
Section titled “Relationship Fields”{ "type": "relationship", "name": "featured_event", "label": "Featured Event", "relationTo": "events", "hasMany": false}{% if block.featured_event %} <a href="/events/{{ block.featured_event.slug }}"> {{ block.featured_event.title }} </a>{% endif %}Group Fields
Section titled “Group Fields”Groups organize related fields together:
{ "type": "group", "name": "cta", "label": "Call to Action", "fields": [ { "type": "text", "name": "text", "label": "Button Text" }, { "type": "text", "name": "url", "label": "Button URL" } ]}{% if block.cta.text and block.cta.url %} <a href="{{ block.cta.url }}" class="btn"> {{ block.cta.text }} </a>{% endif %}Array Fields
Section titled “Array Fields”Arrays allow multiple items of the same structure:
{ "type": "array", "name": "items", "labels": { "singular": "Item", "plural": "Items" }, "fields": [ { "type": "text", "name": "title", "label": "Title" }, { "type": "richtext", "name": "content", "label": "Content" } ]}{% for item in block.items %} <div class="item"> <h3>{{ item.title }}</h3> {{ item.content_html }} </div>{% endfor %}Complete Block Example
Section titled “Complete Block Example”Here’s a comprehensive example of a feature panel block:
{# blocks/feature-panel.liquid #}
<section id="{{ block.id }}" class="feature-panel {% if block.right_align %}feature-panel--right{% endif %}"> <div class="feature-panel__inner">
{% if block.image %} <div class="feature-panel__media"> <img src="{{ block.image | image_url: width: 800, height: 600 }}" alt="{{ block.image.alt | default: block.title }}" > </div> {% endif %}
<div class="feature-panel__content"> {% if block.prefix %} <span class="feature-panel__prefix">{{ block.prefix }}</span> {% endif %}
{% if block.title %} <h2 class="feature-panel__title">{{ block.title }}</h2> {% endif %}
{% if block.details %} <div class="feature-panel__details prose"> {{ block.details_html }} </div> {% endif %}
{% if block.cta.text and block.cta.url %} <a href="{{ block.cta.url }}" class="btn"> {{ block.cta.text }} </a> {% endif %} </div>
</div></section>{% schema %}{ "name": "feature-panel", "singular": "Feature Panel", "plural": "Feature Panels", "label": "Feature Panel", "settings": [ { "type": "text", "name": "prefix", "label": "Prefix" }, { "type": "text", "name": "title", "label": "Title" }, { "type": "upload", "name": "image", "label": "Image", "relationTo": "media" }, { "type": "richtext", "name": "details", "label": "Details" }, { "type": "checkbox", "name": "right_align", "label": "Right Align Image" }, { "type": "group", "name": "cta", "label": "Call to Action", "fields": [ { "type": "text", "name": "text", "label": "Button Text" }, { "type": "text", "name": "url", "label": "Button URL" } ] } ]}{% endschema %}Restricting Blocks to Templates
Section titled “Restricting Blocks to Templates”Templates can specify which blocks are available using the blocks array in their schema:
{# templates/event.liquid #}
{% layout 'layouts/default.liquid' %}
{% capture content_for_layout %} <h1>{{ event.title }}</h1> {% stageblocks event %}{% endcapture %}
{% schema %}{ "name": "event", "settings": [...], "blocks": [ "accordion", "content", "feature-panel", "image-gallery", "video-gallery", "well" ]}{% endschema %}When the blocks array is defined, only those block types will be available when editing content of that type. If omitted, all blocks are available.
Template-Specific Settings
Section titled “Template-Specific Settings”Templates can also define their own settings that become custom fields on the content type. These are accessible via [content].theme:
{# templates/event.liquid #}
{% schema %}{ "name": "event", "settings": [ { "type": "upload", "name": "hero_image", "label": "Hero Image", "relationTo": "media" }, { "type": "group", "name": "primaryBtn", "label": "Primary Button", "fields": [ { "type": "text", "name": "title", "label": "Title" }, { "type": "text", "name": "link", "label": "Link" } ] } ], "blocks": [...]}{% endschema %}Accessing template settings:
{% if event.theme.settings.hero_image %} <img src="{{ event.theme.settings.hero_image | image_url: width: 1200 }}">{% endif %}
{% if event.theme.settings.primaryBtn.title %} <a href="{{ event.theme.settings.primaryBtn.link }}"> {{ event.theme.settings.primaryBtn.title }} </a>{% endif %}Best Practices
Section titled “Best Practices”-
Use semantic names - Block names should describe their purpose (e.g.,
feature-panelnotblock-1) -
Provide helpful labels - Clear labels help editors understand what each field does
-
Group related fields - Use
grouptype to organize related fields together -
Handle empty states - Always check if fields have values before rendering
-
Use unique IDs - Include
{{ block.id }}in section IDs for JavaScript targeting and anchor links -
Document your blocks - Consider adding comments in the template explaining complex logic
-
Test with empty content - Ensure blocks render gracefully when fields are empty