# Shopify theme blocks vs section blocks explained

Theme blocks, nested blocks and content_for vs old section.blocks loops. When to use each in Horizon.

- Date: 2026-08-21
- Category: Shopify

# Shopify theme blocks vs section blocks explained

Shopify theme blocks are the layout unit Horizon actually uses. Section blocks still exist, and a lot of Dawn-era Liquid still loops them, which is why stores get stuck with two mental models in one theme. I am Alan Vo. I build Shopify storefronts from the Gold Coast, including fashion and jewellery catalogues where merchandisers need to rearrange a PDP without a developer. If you only remember one distinction in 2026, remember this: section blocks live inside one section. Theme blocks are files you can reuse and nest.

Shopify documents the model in [theme blocks](https://shopify.dev/docs/storefronts/themes/architecture/blocks). The rest of this note is how I choose between `@theme`, nested blocks and the old `section.blocks` loop on a production theme.

## What Shopify theme blocks actually are

Shopify theme blocks are Liquid files in the theme's `blocks/` directory. Each file is a block type with its own schema, settings and optional nested children. A section (or another block) opts in by accepting theme blocks in schema, then renders whatever the merchant placed with `{% content_for 'blocks' %}`. You do not write a `for` loop over `section.blocks` for those children. Shopify renders the tree.

That is the whole point. A heading block, an image block, a group block and a button block can be composed in the editor into a hero, a PDP module or a collection banner. On Dawn, that composition usually meant a custom section with a hard-coded layout, or a page-builder app. Horizon-family themes assume you will compose in the editor.

Nesting is real and limited. Shopify allows nested theme blocks several levels deep. That is enough to build a product card that contains a media group, a title, a price and a badge. It is also enough to build a monster homepage that no one can debug. Depth is a feature. Uncontrolled depth is how INP dies.

Theme blocks can be static (always present) or merchant-added. Static blocks are how you lock a PDP into a selling order: gallery, title, price, variant picker, buy button, then optional merchandising. Merchant-added blocks are how marketing adds a campaign strip without waiting on a release.

App blocks sit beside this model. They are not theme blocks. They are `@app` entries so an installed app can drop UI into a section. I keep them in a separate conversation: [Shopify app blocks for reviews and scripts](https://alanvo.com/blog/shopify-app-blocks-storefront/). Mixing the two in your head is how a review widget ends up as a custom Liquid snippet again.

![Shopify theme blocks on a laptop showing a Shopify storefront layout](../../images/blog/shopify-theme-blocks-explained-1.jpg)

## What section blocks still are

Section blocks are the older Online Store 2.0 pattern. You declare `blocks` inside a single section's `{% schema %}`. Each block type exists only in that section. You render them by looping `section.blocks` and switching on `block.type`. There is no reuse. A "slide" block in `slideshow.liquid` cannot be dropped into `featured-collection.liquid`.

This model is not illegal. Shopify still renders it. Plenty of Dawn sections still ship this way. I still use section blocks when the block only makes sense in one place and must never nest: a slideshow slide, a menu item in a custom header, a single-purpose promo tile with three locked settings.

The failure mode is treating section blocks as your entire architecture in 2026. Every new layout becomes a new section file. Merchants cannot nest a heading inside a group. Developers copy Liquid between files until nobody knows which slideshow is live. That is the Dawn maintenance trap, and it is why Horizon moved the default to theme blocks.

`content_for` is the render API for the new tree. `{% content_for 'blocks' %}` prints child theme blocks in editor order. You can also target a specific block ID when you need a named slot. If you mix `content_for` and a `section.blocks` loop in the same section without a clear split, you will render some children twice or none at all. Pick one parent model per section.

## When I use Shopify theme blocks on a live store

I use Shopify theme blocks whenever the piece is reusable or the merchant needs to compose layout. Product cards, PDP extra content, homepage rows, collection headers and footer groups almost always qualify. Fashion lookbooks are a good example. The brand wants a full-bleed image, a short caption and a shop-the-look row, in a different order every campaign. That is a group of theme blocks, not a new section every Thursday.

I still write custom theme blocks. Horizon's stock blocks will not know about a jewellery millimetre size, a knitwear gauge note or a furniture finish swatch. Those become small theme blocks that read a metafield and stay nestable. The merchant can place the millimetre block under the title on one collection and under the gallery on another.

I lock the buy path. Theme blocks should not let a merchandiser bury the add-to-cart button under four editorial groups on a PDP. I use static blocks or a preset for the commercial spine, then open theme-block slots above and below for storytelling. Editor freedom without a spine is how a couture site stops selling.

![Clothing rails in a shop used as a live catalogue for Shopify theme blocks](../../images/blog/shopify-theme-blocks-explained-2.jpg)

If you are migrating off Dawn, do not try to translate each section block type into a theme block one-for-one. Translate jobs: "this section showed a product card" becomes a product-card theme block used in three templates. That collapse is the win. I wrote the cutover process as [how to migrate a Dawn store to Shopify Horizon](https://alanvo.com/blog/shopify-horizon-theme-migration/).

## Production checklist for theme blocks vs section blocks

1. List every custom section. Mark each block type as reusable, one-off, or app.
2. Put reusable UI in `blocks/` as theme blocks. One job per file: title, price, media, badge, accordion, CTA.
3. Opt sections into theme blocks only where composition is wanted. A checkout-adjacent cart summary may stay rigid.
4. Render theme-block children with `content_for`, not a copied Dawn loop.
5. Use section blocks only when the type cannot exist anywhere else and must not nest.
6. Cap nesting in presets. If the merchant needs eight levels to build a hero, your blocks are too small or your groups are too vague.
7. Provide presets for PDP, collection header and homepage so the editor opens on a selling layout, not an empty group.
8. Localise every string in theme-block schema. Hard-coded English inside a nested block is how markets break.
9. Keep CSS at the block level. A 2,000-line section stylesheet that assumes one tree will fight nested groups.
10. Test the editor as the merchant: add, nest, reorder, hide. If a setting is only reachable in code, it is not a theme block win.
11. Re-test variant picker, quick add and filters after you nest media blocks. Event targets move.
12. Document for the client which slots are locked (buy path) and which are free (campaign).

## What breaks when you mix the two models

Duplicate renders happen when a section still loops `section.blocks` and also calls `content_for`. You see two headings, or a block in the editor that never appears, or a block that appears but cannot be nested. Split the section or delete the loop.

Settings that used to live on the section move onto blocks and vanish from the merchant's muscle memory. "Where did the overlay opacity go?" It is on the image block now. Train that, or they will paste hex codes into the wrong field and call the theme broken.

![Theme code on a monitor during Shopify theme blocks work](../../images/blog/shopify-theme-blocks-explained-3.jpg)

Performance slips when every card on a collection is a deep tree of theme blocks, each with its own CSS and maybe a tiny script. Dawn's flatter card was one Liquid file. Horizon's card can be five files and a web component. I flatten cards that do not need editor composition. Collection grids are for scanning, not for nested page-building.

App compatibility breaks when a section no longer exposes `@app` or when nested groups wrap the product form. Reviews and wishlists then inject into a group that is not on the buy path. Place app blocks in the static commercial spine, not inside an optional campaign group.

JavaScript that queried `.block` or counted `section.blocks.length` will lie. Theme-block DOM is not the old block wrapper. Bind to documented theme events or to your own block's root.

Accessibility fails when nested groups produce heading soup: an H2 inside an H2 inside a card that is inside a section that already has an H2. Set heading levels in block settings and preset them correctly. Do not let the merchant pick H1 on a product card.

## How I measure whether the block model is working

I measure editor time and storefront honesty. Can a merchandiser build next week's homepage row in the theme editor without a developer, and does the published HTML still have one H1, a working product form and a gallery that is the LCP image with width and height?

On collection pages I watch INP on filter and quick-add clicks. Nested cards that attach listeners per block will lose to a flatter card. If INP regresses after a "more flexible" rebuild, I collapse the card tree.

On PDPs I watch add-to-cart rate and scroll depth only as supporting context. The hard checks are: variant change updates price and media, sold-out disables the button, and metafield blocks read the right product. Theme blocks that show another product's care copy are a data-binding bug, not a design issue.

I also count theme files. If `blocks/` grows past a few dozen near-duplicate files, the model has fragmented. Merge blocks that only differ by a class name.

## Related work on this site

Nested merchandising only pays if photography and the buy path still agree. That is the constraint on the [Tamannaah Fine Jewellery Shopify Plus storefront](https://alanvo.com/work/tamannaah-fine-jewellery/) and on the [Cecilie Bahnsen fashion storefront](https://alanvo.com/work/cecilie-bahnsen/), where lookbook images have to stay in charge and checkout still has to work. Theme blocks are how I would structure that flexibility in 2026. They are not an excuse to turn a couture PDP into a page builder.

![Phone showing a product page in Shopify theme blocks testing](../../images/blog/shopify-theme-blocks-explained-4.jpg)

## FAQ

### Are Shopify theme blocks replacing sections?

No. Sections still wrap a page region. Shopify theme blocks live inside sections (and inside other blocks) as the reusable children. Horizon still uses JSON templates that list sections. The change is what those sections contain.

### When should I keep section blocks instead of Shopify theme blocks?

Keep section blocks when the type is unique to one section and must not nest, such as a slideshow slide with locked fields. Use Shopify theme blocks when the UI is reused or merchants need to compose layout. Do not keep section blocks only because the Dawn file already had them.

### Do Shopify theme blocks hurt Core Web Vitals?

They can. Shopify theme blocks add markup and sometimes extra CSS per nested file. A deep product card on a 48-item collection is a common INP and HTML-weight problem. Use nesting on PDPs and homepages. Keep collection cards flatter unless the merchant truly edits them.

### How do Shopify theme blocks differ from app blocks?

Shopify theme blocks ship in your theme and are your HTML. App blocks ship in a theme app extension and are the app's HTML. A section often accepts both: `@theme` for your blocks and `@app` for reviews or loyalty. Do not rebuild an app's widget as a theme block unless you are replacing the app.


![Card payment at a counter after a Shopify theme blocks release](../../images/blog/shopify-theme-blocks-explained-5.jpg)


HTML version: https://alanvo.com/blog/shopify-theme-blocks-explained/
