Some interesting things I've learned about metaobjects

Hi all,

I thought I’d make my first post about something I’ve learned recently about metaobjects that’s not really well documented. Well, it is and it isn’t documented (explicitly). Either way, I feel I have some learnings that will help shape when you should (and maybe shouldn’t) use metaobjects.

We’re using metaobjects to configure some website functionality. Basically we’re storing country/region information – something we currently do in a JSON-formatted shop metafield, which isn’t very non-technical user friendly.

You could argue that this information could be stored as metafields on markets, however we liked the new Admin interface for metaobjects and being able to shape the data model was a great idea, as well as being able to provide information per country/region (especially if the market comprises multiple countries/regions). We also wanted to store country/region information for areas that we’re not selling to just yet (e.g. a “rest of world” region).

Using and configuring metaobjects is great. The Shopify Admin experience is very good there. It’s easy for non-technical people to use as well for entry management and translations.

One of the first “issues” I hit was using metaobjects in Liquid. It is possible to refer to individual metaobjects using shop.metaobjects[type][handle] and you can use shop.metaobjects[type].values for an iterator over multiple metaobjects. However, you can only refer up to 20 individual metaobjects by handles, and forloop iteration is controlled by Liquid’s pagination limitations.

If you are thinking of using metaobjects for site configuration where you need more than 20-50 configuration objects (e.g. let’s say you’re doing a store locator and you’re storing all store locations as metaobject entries), don’t expect to be able to render all stores within the single page render. I think this architecture makes sense for server-side rendering performance, and you can potentially get around it with smart UI/UX design that downloads additional pages when necessary, depending on what you’re use-case is.

Thankfully, one “hack” I found is that if you need to refer to more than 50 maximum metaobjects, you can use the {% paginate <iterator> by <amount> %} tag. For instance, I have 65 country/region entries, so doing something like this actually works:

{% assign countries = shop.metaobjects.country.values %}
{% paginate countries by 200 %}
  {% for country in countries %}
    # ...
  {% endfor %}
{% endpaginate %}

Eventually we’d love to use metaobjects to manage store locations, but we’ll have some issues due to, again, further limitations that probably just haven’t yet been worked into the platform. For example, if we are limited to maximum 50 entries per page, how do we search/filter/retrieve entries which are based on some kind of query or sorting method? I don’t believe search is available for metaobjects yet, and there might be a way to do it via Storefront API (I’ve not looked into it yet). For example, if I had latitude and longitude values for each store and I managed to create a UI that could produce a LatLng based on a search term and then try to search the metaobject entries for closest locations, it might be a real stretch to do something like that using Shopify.

So my main learnings are this:

  • Metaobjects are primarily for paginated content
  • Using metaobjects in Liquid is subject to maximum handle reference (20) and pagination limitations
  • If you need to do something fancy (e.g. metaobject entry search), you’ll probably have to build your own app — beware of adding additional layers as further complication only leads to operational and development complexity
4 Likes

Do you know if paginating like that works with a layout? I cannot remember as it has been a while but I thought pagination past 50 only worked without a layout, like for partial views you are pulling to render conditionally (not using the Section Rendering API).

Hi mscheurich!

Welcome to the .dev Community :slight_smile:

Thanks for sharing these insights - I’ll make sure the Custom Data product team are informed about the limitations you’re experiencing and your use case.

1 Like

Hi all! Me again. I learned another thing about metaobjects.

Metaobjects don’t act like normal iterators. For example, if you have an object (i.e. hash) you can iterate through the properties using a {% for prop in target %}, e.g.

{% comment %}
Imagine this is the hash:

{
  "testA": 1,
  "testB": 2,
  "testC": 3
}
{% endcomment %}
{
{% for prop in testHash -%}
  {% assign key = prop | first -%}
  {% assign val = prop | last -%}
  {{ key | json }}: {{ val | json }}{% unless forloop.last %},{% endunless -%}
{% endfor -%}
}

Outputs:

{
  "testA": 1,
  "testB": 2,
  "testC": 3
}

Metaobjects are not normal hashes though and don’t seem to respond the same, probably due to supporting different property types, each requiring different handlers:

{
{% for prop in metaobject -%}
  {% assign key = prop | first -%}
  {% assign val = prop | last -%}
  {{ key | json }}: {{ val | json }}{% unless forloop.last %},{% endunless -%}
{% endfor -%}
}

Outputs:

{
}

One workaround I did is to store a list of known metaobject properties and iterate over that:

{% assign known_props = "iso2,iso3,name,currency_code,currency_name" %}
{
{% for prop in known_props -%}
  {% assign key = prop | first -%} 
  {% assign val = metaobject[prop].value -%}
  {{ key | json }}: {{ val | json }}{% unless forloop.last %},{% endunless -%}
{% endfor -%}
}

Note: usage of .value when referring to a specific metaobject property (e.g. metaobject[prop].value) is very important if you’re dealing with any non-string (i.e. list, json or boolean) values.