> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/Shopify/horizon/llms.txt
> Use this file to discover all available pages before exploring further.

# Theme Blocks Architecture

> Deep dive into Horizon's theme blocks system, including nestable blocks, composition patterns, and advanced use cases

## Introduction

Theme blocks are the foundation of Horizon's architecture, enabling unprecedented flexibility and reusability. With **94 theme blocks** in Horizon, this system represents the future of Shopify theme development.

<Note>
  Theme blocks are Liquid files in the `blocks/` directory that can be dynamically added to any section that accepts `{ "type": "@theme" }`. They're prefixed with an underscore (`_`).
</Note>

## Core Concepts

### What Makes a Theme Block?

A theme block is defined by three characteristics:

<Steps>
  <Step title="Location">
    File must be in the `blocks/` directory
  </Step>

  <Step title="Naming">
    Filename must start with an underscore: `_heading.liquid`, `_content.liquid`
  </Step>

  <Step title="Schema">
    Must include a `{% schema %}` tag defining settings and configuration
  </Step>
</Steps>

### Theme Block vs Regular Block

<Tabs>
  <Tab title="Theme Block">
    ```liquid blocks/_heading.liquid theme={null}
    {%- doc -%}
      Renders a heading block.
    {%- enddoc -%}

    {% render 'text', width: '100%', block: block, fallback_text: text %}

    {% schema %}
    {
      "name": "t:names.heading",
      "settings": [
        {
          "type": "richtext",
          "id": "text",
          "label": "t:settings.text"
        }
      ]
    }
    {% endschema %}
    ```

    **Location**: `blocks/` directory\
    **Usage**: Can be added to ANY section with `{ "type": "@theme" }`\
    **Reusability**: Maximum - used across multiple sections
  </Tab>

  <Tab title="Section-Specific Block">
    ```liquid theme={null}
    {%- comment -%} Defined inline in section schema {%- endcomment -%}
    {% for block in section.blocks %}
      {% if block.type == 'heading' %}
        <h2>{{ block.settings.text }}</h2>
      {% endif %}
    {% endfor %}

    {% schema %}
    {
      "blocks": [
        {
          "type": "heading",
          "name": "Heading",
          "settings": [
            { "type": "text", "id": "text" }
          ]
        }
      ]
    }
    {% endschema %}
    ```

    **Location**: Defined in section schema\
    **Usage**: Only available in that specific section\
    **Reusability**: Limited to one section
  </Tab>
</Tabs>

## Horizon's Theme Blocks Catalog

Horizon includes **94 theme blocks** organized into categories:

### Content Blocks

<CardGroup cols={2}>
  <Card title="Text & Headings" icon="font">
    * `_heading.liquid` - Customizable headings (h1-h6)
    * `_inline-text.liquid` - Inline text elements
    * `_content.liquid` - Nestable content groups
    * `_content-without-appearance.liquid` - Content without styling
  </Card>

  <Card title="Dividers & Spacing" icon="minus">
    * `_divider.liquid` - Visual dividers
    * Integrated spacing controls in all blocks
  </Card>
</CardGroup>

### Media Blocks

<CardGroup cols={2}>
  <Card title="Images" icon="image">
    * `_image.liquid` - Responsive images
    * `_media.liquid` - Image/video media
    * `_media-without-appearance.liquid` - Media without container
  </Card>

  <Card title="Advanced Media" icon="film">
    * `_carousel-content.liquid` - Carousel items
    * `_layered-slide.liquid` - Layered slideshow slides
    * `_hotspot-product.liquid` - Interactive hotspots
  </Card>
</CardGroup>

### Product Blocks

<Accordion title="Product Cards (8 blocks)">
  * `_product-card.liquid` - Complete product card
  * `_product-card-gallery.liquid` - Product image gallery
  * `_product-card-group.liquid` - Grouped product cards
  * `_product-details.liquid` - Product information
  * `_featured-product.liquid` - Featured product showcase
  * `_featured-product-gallery.liquid` - Featured product images
  * `_featured-product-price.liquid` - Product pricing
  * `_featured-product-information-carousel.liquid` - Product info carousel
</Accordion>

<Accordion title="Product List Blocks (3 blocks)">
  * `_product-list-content.liquid` - Product list container
  * `_product-list-button.liquid` - Product list actions
</Accordion>

### Collection Blocks

<Accordion title="Collection Components (5 blocks)">
  * `_collection-card.liquid` - Collection card
  * `_collection-card-image.liquid` - Collection image
  * `_collection-image.liquid` - Collection banner image
  * `_collection-info.liquid` - Collection information
  * `_collection-link.liquid` - Collection navigation link
  * `_inline-collection-title.liquid` - Inline collection title
</Accordion>

### Blog Blocks

<Accordion title="Blog Components (8 blocks)">
  * `_blog-post-card.liquid` - Blog post card
  * `_blog-post-content.liquid` - Post content
  * `_blog-post-description.liquid` - Post excerpt
  * `_blog-post-featured-image.liquid` - Post featured image
  * `_blog-post-image.liquid` - Post inline image
  * `_blog-post-info-text.liquid` - Post metadata
  * `_featured-blog-posts-card.liquid` - Featured post card
  * `_featured-blog-posts-image.liquid` - Featured post image
  * `_featured-blog-posts-title.liquid` - Featured post title
</Accordion>

### Cart Blocks

<CardGroup cols={3}>
  <Card title="_cart-products" icon="shopping-basket">
    Cart line items display
  </Card>

  <Card title="_cart-summary" icon="calculator">
    Cart totals and checkout
  </Card>

  <Card title="_cart-title" icon="heading">
    Cart page heading
  </Card>
</CardGroup>

### Navigation Blocks

<Accordion title="Header Blocks (3 blocks)">
  * `_header-logo.liquid` - Site logo with responsive sizing
  * `_header-menu.liquid` - Main navigation menu
  * `_announcement.liquid` - Announcement bar item
</Accordion>

<Accordion title="Footer Blocks (1 block)">
  * `_footer-social-icons.liquid` - Social media icon links
</Accordion>

### Accordion Blocks

<Card title="_accordion-row" icon="chevron-down">
  Collapsible accordion row for FAQs and content organization
</Card>

### Layout Blocks

<CardGroup cols={2}>
  <Card title="_card" icon="rectangle-wide">
    Generic card container
  </Card>

  <Card title="_marquee" icon="arrows-left-right">
    Scrolling marquee text
  </Card>
</CardGroup>

## Block Architecture Patterns

### 1. Self-Contained Blocks

Simple blocks that render complete components:

```liquid blocks/_divider.liquid theme={null}
<div class="divider" style="--divider-height: {{ block.settings.height }}px;"></div>

{% schema %}
{
  "name": "t:names.divider",
  "settings": [
    {
      "type": "range",
      "id": "height",
      "min": 1,
      "max": 100,
      "default": 1
    }
  ]
}
{% endschema %}
```

### 2. Wrapper Blocks with Delegation

Blocks that delegate rendering to snippets:

```liquid blocks/_heading.liquid theme={null}
{%- doc -%}
  Renders a heading block by delegating to the text snippet.
{%- enddoc -%}

{% render 'text', 
  width: '100%', 
  block: block, 
  fallback_text: text 
%}

{% schema %}
{
  "name": "t:names.heading",
  "tag": null,
  "settings": [...]
}
{% endschema %}
```

<Note>
  The `"tag": null` setting prevents Shopify from wrapping the block in a container, giving full control to the snippet.
</Note>

### 3. Nestable Container Blocks

Blocks that accept other blocks as children:

```liquid blocks/_content.liquid theme={null}
{% capture children %}
  {% content_for 'blocks' %}
{% endcapture %}

{% render 'group', 
  children: children, 
  settings: block.settings, 
  shopify_attributes: block.shopify_attributes 
%}

{% schema %}
{
  "name": "t:names.content",
  "tag": null,
  "blocks": [
    { "type": "@theme" },
    { "type": "@app" },
    { "type": "_divider" }
  ],
  "settings": [
    {
      "type": "select",
      "id": "horizontal_alignment_flex_direction_column",
      "label": "t:settings.alignment",
      "options": [
        { "value": "flex-start", "label": "t:options.left" },
        { "value": "center", "label": "t:options.center" },
        { "value": "flex-end", "label": "t:options.right" }
      ]
    },
    {
      "type": "range",
      "id": "gap",
      "label": "t:settings.gap",
      "min": 0,
      "max": 100,
      "unit": "px",
      "default": 24
    },
    {
      "type": "checkbox",
      "id": "inherit_color_scheme",
      "label": "t:settings.inherit_color_scheme",
      "default": true
    },
    {
      "type": "color_scheme",
      "id": "color_scheme",
      "label": "t:settings.color_scheme",
      "default": "scheme-1",
      "visible_if": "{{ block.settings.inherit_color_scheme == false }}"
    }
  ]
}
{% endschema %}
```

## Nesting and Composition

### How Nesting Works

Theme blocks can be nested multiple levels deep:

```
Section (_blocks.liquid)
└── Block: _content
    ├── Block: _heading
    ├── Block: _content (nested!)
    │   ├── Block: _image
    │   └── Block: button
    └── Block: _divider
```

### Rendering Flow

<Steps>
  <Step title="Section renders">
    `_blocks.liquid` section starts rendering
  </Step>

  <Step title="content_for captures blocks">
    `{% content_for 'blocks' %}` captures all child blocks
  </Step>

  <Step title="First-level blocks render">
    `_content` block starts rendering
  </Step>

  <Step title="Nested content_for">
    Nested `_content` block calls `{% content_for 'blocks' %}` again
  </Step>

  <Step title="Nested blocks render">
    `_image` and `button` blocks render inside nested content
  </Step>

  <Step title="Bubbles up">
    All rendered HTML bubbles up to the section
  </Step>
</Steps>

### Practical Example

```liquid theme={null}
{%- comment -%} Section: _blocks.liquid {%- endcomment -%}
{% capture children %}
  {% content_for 'blocks' %}  {%- comment -%} Captures: _content block {%- endcomment -%}
{% endcapture %}

{%- comment -%} Block: _content.liquid {%- endcomment -%}
{% capture children %}
  {% content_for 'blocks' %}  {%- comment -%} Captures: _heading, _image {%- endcomment -%}
{% endcapture %}
```

**Output:**

```html theme={null}
<div class="section">
  <div class="group-block">  <!-- _content -->
    <div class="text-block">
      <h2>Welcome</h2>  <!-- _heading -->
    </div>
    <div class="image-block">
      <img src="hero.jpg">  <!-- _image -->
    </div>
  </div>
</div>
```

## Static vs Dynamic Blocks

### Static Blocks

Always present, cannot be removed:

```json sections/header.liquid (schema) theme={null}
{
  "blocks": {
    "header-logo": {
      "type": "_header-logo",
      "static": true,
      "settings": {
        "hide_logo_on_home_page": false
      }
    },
    "header-menu": {
      "type": "_header-menu",
      "static": true,
      "settings": {
        "menu": "main-menu"
      }
    }
  }
}
```

Rendered using `content_for 'block'`:

```liquid theme={null}
{% content_for 'block', type: '_header-logo', id: 'header-logo' %}
{% content_for 'block', type: '_header-menu', id: 'header-menu' %}
```

### Dynamic Blocks

Can be added, removed, reordered by merchants:

```json templates/index.json theme={null}
{
  "sections": {
    "hero": {
      "type": "hero",
      "blocks": {
        "text_abc123": {
          "type": "_heading",
          "settings": { "text": "<h1>Welcome</h1>" }
        },
        "button_xyz789": {
          "type": "button",
          "settings": { "label": "Shop Now" }
        }
      },
      "block_order": ["text_abc123", "button_xyz789"]
    }
  }
}
```

Rendered using `content_for 'blocks'`:

```liquid theme={null}
{% capture children %}
  {% content_for 'blocks' %}  {%- comment -%} Renders all dynamic blocks {%- endcomment -%}
{% endcapture %}
```

## Advanced Features

### Context Passing

Pass data to static blocks:

```liquid sections/product-list.liquid theme={null}
{% for product in collection.products %}
  {% content_for 'block', 
    type: '_product-card', 
    id: 'static-product-card',
    closest.product: product,
    closest.collection: collection 
  %}
{% endfor %}
```

Access in block:

```liquid blocks/_product-card.liquid theme={null}
<div class="product-card">
  <h3>{{ closest.product.title }}</h3>
  <p>{{ closest.product.price | money }}</p>
  <a href="{{ closest.product.url }}">View Product</a>
</div>
```

### Shopify Attributes

Preserve theme editor functionality:

```liquid blocks/_content.liquid theme={null}
<div
  class="group-block"
  {{ shopify_attributes }}  {%- comment -%} Critical for theme editor! {%- endcomment -%}
>
  {{ children }}
</div>
```

<Warning>
  Always include `{{ shopify_attributes }}` in the root element of a block, or the theme editor won't be able to highlight and edit the block.
</Warning>

### Conditional Settings with visible\_if

```json blocks/_heading.liquid (schema) theme={null}
{
  "settings": [
    {
      "type": "checkbox",
      "id": "background",
      "label": "t:settings.background",
      "default": false
    },
    {
      "type": "color",
      "id": "background_color",
      "label": "t:settings.background_color",
      "alpha": true,
      "default": "#00000026",
      "visible_if": "{{ block.settings.background }}"
    },
    {
      "type": "range",
      "id": "corner_radius",
      "label": "t:settings.corner_radius",
      "min": 0,
      "max": 50,
      "default": 0,
      "visible_if": "{{ block.settings.background }}"
    }
  ]
}
```

### Read-only Settings

Hide settings from merchants while preserving functionality:

```json theme={null}
{
  "settings": [
    {
      "type": "checkbox",
      "id": "read_only",
      "label": "t:settings.read_only",
      "visible_if": "{{ false }}",
      "default": false
    },
    {
      "type": "richtext",
      "id": "text",
      "label": "t:settings.text",
      "visible_if": "{{ block.settings.read_only != true }}"
    }
  ]
}
```

## Creating Custom Theme Blocks

### Basic Theme Block Template

```liquid blocks/_custom-block.liquid theme={null}
{%- doc -%}
  Description of what this block does.

  @param {string} setting_name - Description of setting
{%- enddoc -%}

<div 
  class="custom-block" 
  {{ shopify_attributes }}
  style="
    --custom-property: {{ block.settings.custom_setting }};
  "
>
  {%- comment -%} Block content here {%- endcomment -%}
</div>

{% schema %}
{
  "name": "t:names.custom_block",
  "tag": null,
  "settings": [
    {
      "type": "header",
      "content": "t:content.settings"
    },
    {
      "type": "text",
      "id": "custom_setting",
      "label": "t:settings.custom_setting",
      "default": "Default value"
    }
  ]
}
{% endschema %}
```

### Nestable Block Template

```liquid blocks/_custom-container.liquid theme={null}
{%- doc -%}
  A container block that accepts nested theme blocks.
{%- enddoc -%}

{% capture children %}
  {% content_for 'blocks' %}
{% endcapture %}

<div 
  class="custom-container" 
  {{ shopify_attributes }}
>
  {{ children }}
</div>

{% schema %}
{
  "name": "t:names.custom_container",
  "tag": null,
  "blocks": [
    { "type": "@theme" },
    { "type": "@app" }
  ],
  "settings": [
    {
      "type": "range",
      "id": "gap",
      "label": "t:settings.gap",
      "min": 0,
      "max": 100,
      "unit": "px",
      "default": 16
    }
  ]
}
{% endschema %}
```

## Best Practices

<AccordionGroup>
  <Accordion title="Keep blocks focused and single-purpose">
    Each block should do one thing well. Prefer composition over monolithic blocks.

    **Good**: `_heading.liquid`, `_image.liquid`, `_button.liquid`\
    **Bad**: `_hero-with-everything.liquid`
  </Accordion>

  <Accordion title="Always include {{ shopify_attributes }}">
    This is critical for theme editor functionality:

    ```liquid theme={null}
    <div {{ shopify_attributes }}>
      <!-- Block content -->
    </div>
    ```
  </Accordion>

  <Accordion title="Document with {%- doc -%} tags">
    ```liquid theme={null}
    {%- doc -%}
      Clear description of the block's purpose.

      @param {type} name - Parameter description
    {%- enddoc -%}
    ```
  </Accordion>

  <Accordion title="Use tag: null for custom rendering">
    Prevents Shopify's default wrapper:

    ```json theme={null}
    {
      "name": "Custom Block",
      "tag": null,
      "settings": [...]
    }
    ```
  </Accordion>

  <Accordion title="Leverage visible_if for better UX">
    Hide irrelevant settings:

    ```json theme={null}
    {
      "id": "advanced_setting",
      "visible_if": "{{ block.settings.enable_advanced }}"
    }
    ```
  </Accordion>

  <Accordion title="Use inheritance for color schemes">
    Allow blocks to inherit parent colors:

    ```json theme={null}
    {
      "type": "checkbox",
      "id": "inherit_color_scheme",
      "default": true
    }
    ```
  </Accordion>
</AccordionGroup>

## Common Patterns

### Pattern: Block with Optional Link

```liquid blocks/_card.liquid theme={null}
<div class="card" {{ shopify_attributes }}>
  {%- if block.settings.link != blank -%}
    <a href="{{ block.settings.link }}" class="card__link"></a>
  {%- endif -%}

  {{ children }}
</div>

{% schema %}
{
  "settings": [
    {
      "type": "url",
      "id": "link",
      "label": "t:settings.link"
    },
    {
      "type": "checkbox",
      "id": "open_in_new_tab",
      "label": "t:settings.open_in_new_tab",
      "visible_if": "{{ block.settings.link != blank }}"
    }
  ]
}
{% endschema %}
```

### Pattern: Block with Media Background

```liquid blocks/_media-block.liquid theme={null}
<div class="media-block" {{ shopify_attributes }}>
  <div class="media-block__background">
    {% render 'background-media',
      background_media: block.settings.background_media,
      background_video: block.settings.video,
      background_image: block.settings.background_image
    %}
  </div>

  {% capture children %}
    {% content_for 'blocks' %}
  {% endcapture %}

  <div class="media-block__content">
    {{ children }}
  </div>
</div>
```

### Pattern: Block with Responsive Settings

```json theme={null}
{
  "settings": [
    {
      "type": "select",
      "id": "width",
      "label": "t:settings.width_desktop",
      "options": [
        { "value": "25%", "label": "25%" },
        { "value": "50%", "label": "50%" },
        { "value": "100%", "label": "100%" }
      ],
      "default": "100%"
    },
    {
      "type": "select",
      "id": "width_mobile",
      "label": "t:settings.width_mobile",
      "options": [
        { "value": "100%", "label": "100%" },
        { "value": "50%", "label": "50%" }
      ],
      "default": "100%"
    }
  ]
}
```

## Debugging Theme Blocks

### Inspecting Block Data

```liquid theme={null}
{%- comment -%} Temporary debugging code {%- endcomment -%}
<script>
  console.log({
    blockType: '{{ block.type }}',
    blockId: '{{ block.id }}',
    settings: {{ block.settings | json }}
  });
</script>
```

### Visual Preview Mode Detection

```liquid theme={null}
<div
  class="block"
  {% if request.visual_preview_mode %}
    data-shopify-visual-preview
  {% endif %}
  {{ shopify_attributes }}
>
```

## Migration Guide

### Converting Section Blocks to Theme Blocks

<Tabs>
  <Tab title="Before (Section-specific)">
    ```liquid sections/hero.liquid theme={null}
    {% for block in section.blocks %}
      {% case block.type %}
        {% when 'heading' %}
          <h2>{{ block.settings.text }}</h2>
        {% when 'button' %}
          <a href="{{ block.settings.link }}">{{ block.settings.label }}</a>
      {% endcase %}
    {% endfor %}

    {% schema %}
    {
      "blocks": [
        {
          "type": "heading",
          "name": "Heading",
          "settings": [{ "type": "text", "id": "text" }]
        },
        {
          "type": "button",
          "name": "Button",
          "settings": [
            { "type": "text", "id": "label" },
            { "type": "url", "id": "link" }
          ]
        }
      ]
    }
    {% endschema %}
    ```
  </Tab>

  <Tab title="After (Theme blocks)">
    ```liquid sections/hero.liquid theme={null}
    {% capture children %}
      {% content_for 'blocks' %}
    {% endcapture %}

    {% render 'section', section: section, children: children %}

    {% schema %}
    {
      "blocks": [
        { "type": "@theme" },
        { "type": "@app" }
      ]
    }
    {% endschema %}
    ```

    ```liquid blocks/_heading.liquid theme={null}
    {% render 'text', block: block %}
    {% schema %}
    {
      "name": "Heading",
      "settings": [{ "type": "richtext", "id": "text" }]
    }
    {% endschema %}
    ```

    ```liquid blocks/_button.liquid theme={null}
    <a href="{{ block.settings.link }}" class="button">
      {{ block.settings.label }}
    </a>
    {% schema %}
    {
      "name": "Button",
      "settings": [
        { "type": "text", "id": "label" },
        { "type": "url", "id": "link" }
      ]
    }
    {% endschema %}
    ```
  </Tab>
</Tabs>

## Performance Considerations

<AccordionGroup>
  <Accordion title="Minimize nesting depth">
    While nesting is powerful, excessive depth can impact render performance. Aim for 2-3 levels maximum.
  </Accordion>

  <Accordion title="Use static blocks for fixed structure">
    Static blocks render faster than dynamic blocks since their position is predetermined.
  </Accordion>

  <Accordion title="Lazy load images in blocks">
    ```liquid theme={null}
    {{ block.settings.image | image_url: width: 800 | image_tag: loading: 'lazy' }}
    ```
  </Accordion>

  <Accordion title="Scope CSS with {% stylesheet %}">
    Only loads when the block is used:

    ```liquid theme={null}
    {% stylesheet %}
      .custom-block { /* styles */ }
    {% endstylesheet %}
    ```
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Theme Structure" icon="folder-tree" href="/architecture/theme-structure">
    Explore sections, snippets, and templates
  </Card>

  <Card title="Development Guide" icon="code" href="/development/workflow">
    Start building custom theme blocks
  </Card>

  <Card title="Block Reference" icon="book" href="/components/blocks/overview">
    Browse all 94 theme blocks
  </Card>

  <Card title="Liquid Storefronts" icon="droplet" href="/architecture/liquid-storefronts">
    Learn modern Liquid features
  </Card>
</CardGroup>
