Using metafields in your Aqua theme

Markdown

View as Markdown

Using metafields in your Aqua theme

Once you’ve defined a metafield and set a value, exposing it in your storefront takes a single line of Aqua. This guide covers the access pattern, type-aware rendering, lists, fallbacks, and the most common pitfalls.

The access pattern

Every resource drop in Aqua exposes a .metafields attribute, namespaced by definition:

{{ owner.metafields.<namespace>.<key> }}

If the metafield exists, this prints the type-correct string. If it does not, it prints an empty string — no exception, so it is safe to use unconditionally in your templates.

A real example

Say you defined three metafields on the product resource:

  • custom.warranty_yearsnumber_integer
  • custom.care_instructionsrich_text_field
  • custom.tagslist.single_line_text_field

Render them on the product page like this:

{% if product.metafields.custom.warranty_years %}
  <p>Warranty: {{ product.metafields.custom.warranty_years }} years</p>
{% endif %}

<div class="care-instructions">
  {{ product.metafields.custom.care_instructions }}
</div>

<ul class="tags">
  {% for tag in product.metafields.custom.tags %}
    <li>{{ tag }}</li>
  {% endfor %}
</ul>

Type-aware rendering

The drop chooses its output based on the metafield type. You don’t need to coerce values yourself:

  • text → the string itself.
  • rich_text_field → raw HTML (do not pipe through escape).
  • number_integer / number_decimal → numeric string.
  • booleantrue or false.
  • color#RRGGBB.
  • weight / dimension / volume<value> <unit>, e.g. 1.5 KILOGRAMS.
  • money → object — always pipe through money.
  • list.* → iterable of value drops.
  • file_reference → the file’s URL (image and video files also expose .src, .alt, and dimensions).
  • other references (product, variant, collection, customer, page) → the raw ID string — see the note below.

Working with money values

<p>Deposit required: {{ product.metafields.custom.deposit | money }}</p>

The money filter respects the shopper’s current currency and locale, so the same metafield renders correctly across every storefront.

Working with references

Reference metafields (such as product_reference, variant_reference, collection_reference, customer_reference and page_reference) currently render the raw ID of the referenced resource in the theme — they are not automatically expanded into a full drop. That means accessing fields like .url or .title on a reference prints an empty string:

{# This prints the stored ID, not a product object #}
{{ product.metafields.custom.related_product }}

{# .url and .title resolve to empty — references aren't expanded #}
<a href="{{ product.metafields.custom.related_product.url }}">...</a>

If you need the referenced resource’s title, handle, or URL, store those values directly (for example in a single_line_text_field or url metafield), or look the resource up another way — for instance via a section AJAX call.

The one exception is file_reference, which does resolve to a usable value: it renders the file’s URL, and image or video files additionally expose fields such as .src, .alt, and dimensions.

<img src="{{ product.metafields.custom.spec_sheet }}" alt="Spec sheet">

Inspecting type and value explicitly

If your block needs to branch on the type, the drop exposes both .value and .type:

{{ product.metafields.custom.warranty_years.type }}   {# 'number_integer' #}
{{ product.metafields.custom.warranty_years.value }}  {# 5 #}

Fallback handling

An unset metafield returns an empty string — not nil — so use {% if %} or the default filter:

{{ product.metafields.custom.badge | default: 'NEW' }}

Where it doesn’t work

  • Inside checkout extensions you must request the data through the App Bridge — the storefront drop is not available there.
  • Inside section schemas (the JSON block of a section file) you cannot reference metafields — schemas are static.

For everything else — product, collection, blog, cart, page, order — the access pattern above works.

FAQ

Why does my metafield print blank in the theme?

Three common causes: (1) the value is genuinely empty, (2) the namespace/key in Aqua doesn’t match the definition exactly, or (3) you saved the value but the page cache is stale. After saving a metafield, hard-refresh the storefront or wait up to 60 seconds for caches to expire. Note that reference metafields print the referenced resource’s ID rather than a drop, so reading .url or .title on one will also appear blank.

How do I escape rich text output?

You don’t. rich_text_field is intentionally raw HTML. If you want to print it as plain text, pipe through strip_html: {{ product.metafields.custom.notes | strip_html }}.

Can I read another resource’s metafields from a product page?

Yes — shop.metafields is always available. For other resources, fetch them via a section AJAX call — a reference metafield only gives you the target’s ID, not its metafields.

How do I sort a list metafield?

List metafields preserve the order you entered them in. Use the sort filter if you need a different order: {% assign sorted = product.metafields.custom.tags | sort %}.

Do metafields work in customizer preview?

Yes. The customizer preview uses the same storefront pipeline, so any metafield set on the resource appears in the preview immediately after save.

What’s the right namespace for an app I’m building?

Pick a unique namespace tied to your app’s handle, e.g. my_app or foundry_reviews. Avoid custom — that’s reserved for merchant-defined fields and clashes are likely.

Was this article helpful?