Start building

Guides

How to add blocks to a Shopify section

Blocks are what let a merchant add, remove and reorder the pieces of a section without touching code. Here is how to define them, render them and make the theme editor treat them properly.

The Klorr teamUpdated 8 min read

  • Liquid
  • Schema
  • Blocks
  • Theme editor

Key takeaways

  • Settings configure the section once; blocks are repeatable pieces the merchant can add, remove and reorder.
  • Each block type is declared in the blocks array of {% schema %} with a type, a name and its own settings.
  • Loop section.blocks, branch on block.type, and put {{ block.shopify_attributes }} on each block's outer element.
  • max_blocks caps the total; limit caps one block type. Presets can ship default blocks so the section never starts empty.

A section without blocks is a fixed layout: the merchant can change its words and pictures, but not how many there are. Blocks fix that. A testimonial slider, an FAQ, a feature grid or a logo strip are all sections whose content is a list, and blocks are how Shopify lets that list grow and shrink from the theme editor. This guide covers how blocks work in Online Store 2.0 themes, with code you can paste.

Blocks vs settings

Every section has two kinds of editable data. Section settings belong to the section as a whole: a heading, a background colour, the number of columns. There is exactly one of each. Blocks are repeatable units inside the section, each with its own settings: one block per slide, per question, per logo.

Section settingsBlocks
How manyOne of eachAs many as the merchant adds, up to your limits
ReorderableNoYes, by drag and drop in the sidebar
Read in Liquid assection.settings.headingblock.settings.heading inside a loop
Good forLayout, colours, the section titleSlides, cards, questions, list items

A useful rule: if you catch yourself creating settings named heading_1, heading_2, heading_3, you wanted a block.

Define block types in the schema

Blocks are declared in the blocks array of the section's {% schema %} tag. Each entry is a block type with a type (the identifier you check in Liquid), a name (what the merchant sees in the editor) and an optional settings array that works exactly like section settings.

sections/faq.liquid (schema)
{% schema %}
{
  "name": "FAQ",
  "max_blocks": 20,
  "settings": [
    { "type": "text", "id": "heading", "label": "Heading", "default": "Questions" }
  ],
  "blocks": [
    {
      "type": "question",
      "name": "Question",
      "settings": [
        { "type": "text", "id": "question", "label": "Question", "default": "Do you ship abroad?" },
        { "type": "richtext", "id": "answer", "label": "Answer", "default": "<p>Yes, to most countries.</p>" }
      ]
    },
    {
      "type": "contact",
      "name": "Contact prompt",
      "limit": 1,
      "settings": [
        { "type": "text", "id": "text", "label": "Text", "default": "Still stuck?" },
        { "type": "url", "id": "link", "label": "Link" }
      ]
    }
  ],
  "presets": [
    {
      "name": "FAQ",
      "blocks": [
        { "type": "question" },
        { "type": "question", "settings": { "question": "How long does delivery take?" } },
        { "type": "contact" }
      ]
    }
  ]
}
{% endschema %}

Block type values must be unique within the section, and block setting ids must be unique within their block. Two different block types can both have a setting called heading; one block type cannot have it twice. If you would rather not type the JSON by hand, the section schema generator builds and checks it in the browser.

max_blocks and limit

  • `max_blocks` sits at the top level of the schema and caps the total number of blocks of all types. Shopify currently allows at most 50 blocks per section, which is also the default when you leave it out.
  • `limit` sits on an individual block type and caps how many of that type can be added. In the example, the merchant can add many questions but only one contact prompt.
  • Once a cap is reached, the editor greys out the block in the "Add block" menu. Nothing breaks; the option simply disappears until a block is removed.

Render blocks with section.blocks

In the markup, loop over section.blocks and branch on block.type. A case statement keeps each block type's markup separate and readable.

sections/faq.liquid (markup)
<section class="faq faq--{{ section.id }}">
  {%- if section.settings.heading != blank -%}
    <h2 class="faq__heading">{{ section.settings.heading | escape }}</h2>
  {%- endif -%}

  {%- for block in section.blocks -%}
    {%- case block.type -%}
      {%- when 'question' -%}
        <details class="faq__item" {{ block.shopify_attributes }}>
          <summary>{{ block.settings.question | escape }}</summary>
          <div class="faq__answer">{{ block.settings.answer }}</div>
        </details>

      {%- when 'contact' -%}
        <p class="faq__contact" {{ block.shopify_attributes }}>
          {%- if block.settings.link != blank -%}
            <a href="{{ block.settings.link }}">{{ block.settings.text | escape }}</a>
          {%- else -%}
            {{ block.settings.text | escape }}
          {%- endif -%}
        </p>
    {%- endcase -%}
  {%- endfor -%}
</section>

Blocks render in the order the merchant arranged them in the sidebar, so the loop needs no sorting. Each block also has a block.id, which is handy for unique element IDs (for example id="Slide-{{ block.id }}") when two copies of the section sit on one page.

Why block.shopify_attributes matters

{{ block.shopify_attributes }} outputs a few data- attributes, but only inside the theme editor. They tell the editor which element belongs to which block, which is what lets the merchant click a slide in the preview and land on its settings, and lets the preview scroll to and highlight a block when it is selected in the sidebar. On the live storefront it outputs nothing, so it costs nothing.

Put it on the outermost element of each block, once. Not on an inner span, and not on the section wrapper. If a block's markup has no single wrapper, add one. Leaving it out is the most common reason a section "works" but feels broken to edit: clicking a block in the preview does nothing.

Block settings

Block settings support the same input types as section settings: text, richtext, image_picker, url, color, range, select, checkbox, product, collection and the rest. Read them with block.settings.<id>. Two habits help:

  • Give every text setting a default, so a new block shows something meaningful instead of an empty box.
  • Check optional settings with != blank before rendering their wrapper, so an empty link or image leaves no empty tag behind.

For a fuller tour of which setting type to use when, see how to make a Shopify section editable.

Presets with default blocks

A section only appears in the theme editor's "Add section" list if its schema has presets. A preset can also include a blocks array, so the section arrives already filled in rather than as an empty shell. Each preset block names a type and can override that block's defaults through settings, as the FAQ example does for its second question.

App blocks (@app)

Some apps ship their own blocks, such as review stars or a subscription picker. A section can accept them by adding a block type of "type": "@app" to its blocks array. App blocks take no name or settings from you; the app defines those. In the loop, render them with {% render block %}:

section.liquid
{%- when '@app' -%}
  {% render block %}

Only add @app where an app block genuinely makes sense, typically main product and collection sections or a general-purpose content section. Newer themes can also accept reusable theme blocks (@theme), defined in the theme's blocks folder; that is a separate system and worth reading Shopify's documentation on before you mix the two.

Common mistakes with section blocks

  1. Missing `{{ block.shopify_attributes }}` — the section renders, but clicking a block in the preview does nothing.
  2. A `when` that doesn't match a `type` — when 'slide' against a block declared as "type": "Slide". Types are case-sensitive, and an unmatched block silently renders nothing.
  3. Escaping richtext — richtext already outputs HTML, so | escape on it prints the tags as text. Escape plain text settings, not richtext.
  4. Duplicate IDs across copies of the section — use section.id and block.id in element IDs and scoped CSS.
  5. JavaScript that only reads blocks on page load — the editor adds, removes and reorders blocks without a reload. Listen for the editor's shopify:block:select and shopify:section:load events if your script builds a slider or tabs.
  6. No presets — the section is valid but never appears in "Add section".

A faster way to get blocks right

None of this is hard, but it is fiddly, and one wrong type name or missing attribute is enough to make a section awkward to edit. If you would rather describe the section than type the schema, Klorr writes it from a plain-English description, with block types, limits, presets and block.shopify_attributes already wired up; the Shopify Liquid code generator page shows what it produces. Either way, once the file exists, adding a custom section to Shopify takes a few minutes.

Questions, answered

What is the difference between blocks and settings in a Shopify section?

Settings belong to the section as a whole and there is one of each, such as a heading or background colour. Blocks are repeatable pieces inside the section, each with its own settings, that the merchant can add, remove and reorder. Use blocks whenever the content is a list.

What does block.shopify_attributes do?

It outputs data attributes in the theme editor that link an element to its block. That lets the merchant click a block in the preview to open its settings, and lets the editor highlight a block selected in the sidebar. It outputs nothing on the live store, so always include it on each block's outer element.

How many blocks can a Shopify section have?

Shopify currently allows up to 50 blocks per section. You can set a lower total with max_blocks, and cap a single block type with limit. The editor hides the option to add more once a cap is reached.

Why are my Shopify blocks not showing on the page?

The usual cause is a mismatch between the type in the schema and the value in your when or if check; types are case-sensitive. Also check that the block's markup is inside the for loop over section.blocks and that the block was actually added in the editor or in the preset.

How do I add app blocks to a custom section?

Add a block entry with "type": "@app" to the section's blocks array, then render it in your loop with {% render block %}. The app supplies the block's name and settings, so you don't define any.

Get the whole section written for you

Describe the section you want. Klorr writes the Liquid, the CSS, the schema and every theme-editor setting — code that lives in your theme and stays yours.