A Metaobject Overview Grid for the Horizon Theme on Shopify
A reusable, dynamic overview grid for Shopify with metaobjects as the data source.

Arno Breitfuss
Full-Stack Developer @ Digit-One
Sections on Shopify, such as a grid on an overview page, used to be painfully static: one layout, hardcoded in Liquid, changeable only by a developer. With metaobjects and theme blocks, it is finally possible to make, for example, such overviews dynamic. We explain how we built such a metaobject overview grid for the Horizon theme.
A grid for more than just events
Once again, one of our projects needed a grid for an overview page, this time for events. Instead of tailoring it to that one case, we wanted to build a metaobject overview grid so it could be reused for any metaobject.
The events consist of:
- Title & image
- Short description
- Date including time
- Exact address
That is already a very specific use case. If the same grid later shows product manufacturers, for example, the address may still fit as the company address. A date, however, makes no sense at all for manufacturers.
So the grid has to be built in a way that:
- the filters can be chosen freely
- the cards are freely configurable
- the initial sort order is easy to change
Here is the grid in the editor: the metaobject type is selectable, filters are configurable as theme blocks, and the cards are made of theme blocks as well.

Filters as theme blocks
On the collection page, Shopify provides the filters. Search & Discovery only knows products, though, and there is no facet API for metaobjects. The filters are therefore theme blocks of their own, dragged into the section in the theme editor:
_metaobject-filter-textfor values such as city or category_metaobject-filter-datefor a from/to date range
Each block has two settings: a label and the handle of the field it filters on. For events, that is a text filter on ort_stadt and a date filter on datum. For a manufacturer grid, the text filter stays and the date filter is simply dropped. No code changes.
While rendering, the text filter collects every value of the field across all entries, including list fields, removes duplicates regardless of upper and lower case, and turns them into checkboxes. In addition, each filter writes a JSON map into the page that records which entry has which values:
{
"yoga-am-see": ["salzburg"],
"staedte-lauf": ["linz"]
}Special filters, like the date for events, can then be implemented just as easily:

The filter state is simply stored in the query string and read with JavaScript:
?ort_stadt=musterhausen&datum_from=2026-10-01&datum_to=2026-10-31Horizon's product card as the model
Horizon already shows how cards can be composed from theme blocks. The product list does this by passing each card its product as context:
{% content_for 'block',
type: '_product-card',
id: 'static-product-card',
closest.product: product
%}With static-product-card, the block ID is static and tells Shopify that one configuration of the card applies to all products. The _product-card type passes closest.product on to its blocks: gallery, title, price, text. A text block knows nothing about the loop. The text block's dynamic source points at the current product, and every card in the grid follows.
The same card, a different object
But closest also accepts a metaobject, addressed by the handle of its definition. For events:
{% content_for 'block',
type: '_metaobject-card',
id: 'static-metaobject-card',
closest.metaobject.veranstaltung: entry
%}Unlike a product, though, a metaobject only has the fields someone has defined. That is why _metaobject-card is only the shell, passing the entry through to Horizon's own blocks:
{% content_for 'blocks', closest.metaobject.veranstaltung: entry %}Because the block ID is static again, this composition is stored once on the section:

The date sets the order
The metaobject overview grid can be extended with custom sort orders. If none is selected, entries are sorted alphabetically. For product manufacturers, for example, that is enough.
For events, there is a sort order by date. It is enabled with a checkbox in the section settings and takes the handle of the date field, datum by default. The next event then comes first, and past events drop out, both from the grid and from the filters.
In exactly the same way, the grid could be extended with any other default sort order!
Where Liquid hits its limits
In a single pass, metaobjects[type].values returns at most 250 entries. That is enough for our use case, but since past events should not be shown, it is up to the store owner to delete outdated entries.
Past events are still counted among the 250 and cannot be filtered out beforehand!
Conclusion
With theme blocks and the closest convention, Horizon brings everything needed to display metaobjects as flexibly as products.
Cards, filters and sorting are put together in the theme editor instead of being hardcoded in Liquid, and the same grid can be reused for events, manufacturers or locations. The filters follow the collection page and feel familiar to customers.
This takes load off development and gives merchants the freedom to design overview pages themselves. The limits lie with very large amounts of data, but for most use cases this makes metaobjects in Horizon a big step towards a full-fledged content management system.