Shopify metafields
not showing.
The metafield is set in admin, the theme block is added, and the product page renders nothing, or worse, prints [object Object] where the value should be. Almost always a namespace typo or a structured-type mismatch. Five ranked causes below, a Liquid debug snippet that dumps every metafield on the product, and a type-aware renderer that handles rich text, lists, and references correctly.
Symptoms
- Block renders empty: the metafield label shows but no value, even though the admin shows a value entered.
- Output is
[object Object]or shows the raw JSON of a structured field instead of formatted text. - Rich text loses formatting: bold, links, and paragraphs disappear; output is one flat line.
- List metafield prints comma-separated IDs (e.g.
gid://shopify/Product/123,gid://shopify/Product/456) instead of titles or images. - Some products show, others don't: the metafield is set on a few products and empty on the rest.
Liquid debug dump
Drop this snippet into your theme, render it on the product page, then load any product with ?debug=metafields in the URL. A pinned debug box shows every metafield available on the product and the variant, including the exact product.metafields.<namespace>.<key> reference Liquid expects.
{% comment %}
Jelonyx · Metafield debug dump
Drop into snippets/jlx-metafield-debug.liquid and render at the bottom of
sections/main-product.liquid (above {% schema %}).
Visible only when the URL includes ?debug=metafields
Source: jelonyx.com/shopify/fix/metafields-not-showing
{% endcomment %}
{%- if request.path contains '/products/' and request.host -%}
{%- assign show = false -%}
{%- if template contains 'product' and request.design_mode -%}{%- assign show = true -%}{%- endif -%}
{%- assign qs = request.path | append: '?' | append: request.search -%}
{%- comment -%}
Liquid has no easy access to query strings. Use a hidden marker:
add ?debug=metafields and the inline JS below toggles a CSS class.
{%- endcomment -%}
<pre id="jlx-mf-debug" hidden style="
position: fixed; bottom: 12px; right: 12px; max-width: 480px; max-height: 60vh;
overflow: auto; padding: 14px 16px; z-index: 99999;
background: #101218; color: #B8D67A; border: 1px solid #2A2D38;
font: 12px/1.5 ui-monospace, Menlo, monospace; white-space: pre-wrap;
">
{%- assign global = product.metafields -%}
PRODUCT METAFIELDS: {{ product.handle }}
{% for ns_pair in global -%}
{%- assign ns = ns_pair[0] -%}
{%- assign keys = ns_pair[1] -%}
{% for kv in keys -%}
{%- assign k = kv[0] -%}
{%- assign v = kv[1] -%}
[{{ ns }}.{{ k }}]
type: {{ v.type }}
ref: product.metafields.{{ ns }}.{{ k }}
value: {{ v.value | json }}
{% endfor %}
{%- endfor %}
VARIANT METAFIELDS: {{ product.selected_or_first_available_variant.id }}
{% for ns_pair in product.selected_or_first_available_variant.metafields -%}
{%- assign ns = ns_pair[0] -%}
{%- for kv in ns_pair[1] -%}
[{{ ns }}.{{ kv[0] }}]
type: {{ kv[1].type }}
ref: variant.metafields.{{ ns }}.{{ kv[0] }}
value: {{ kv[1].value | json }}
{% endfor %}
{%- endfor %}
</pre>
<script>
(function () {
if (location.search.indexOf('debug=metafields') !== -1) {
var el = document.getElementById('jlx-mf-debug');
if (el) el.hidden = false;
}
})();
</script>
{%- endif -%}Render at the bottom of sections/main-product.liquid:
{% render 'jlx-metafield-debug' %}- Debug box empty → the product has no metafields (cause #3) or the definition is on the wrong resource (set on variant when theme reads from product, or vice versa).
- Reference looks like
app--xxxx--key→ app-managed metafield, not directly accessible (cause #5). - Type is
rich_text_fieldor starts withlist.→ structured type, needs the type-aware renderer below (cause #2). - Reference exists but theme uses a different one → namespace typo (cause #1). Update the Liquid reference to match.
Most likely causes, ranked
- 01Wrong namespace or key in the Liquid reference.
The admin labels metafields with a friendly name like Product spec, but Liquid needs the exact
namespace.keypair from the definition. A developer who guessedcustom.specwhen the definition is actuallyspecs.productgets silent empty output. Frequency: most common.Fix: open Settings → Custom data → Products and click the metafield definition. The exact namespace and key are shown in the URL and on the definition page. Update every Liquid reference to match. Use the debug dump above to confirm.
- 02Structured type rendered as a raw string.
Rich text returns a JSON object.
{{ mf.value }}prints[object Object]. Lists return arrays, same problem. References return another resource and need a follow:mf.value.titlefor products,mf.value | image_urlfor files.Fix: for rich text, use
{{ mf | metafield_tag }}. For lists, loop with{% for item in mf.value %}. For references, follow the value before output. The renderer snippet below handles every type: pass any metafield to it and it picks the right output. - 03Specific product has no value assigned.
The definition exists, the theme reference is correct, but the product itself was added before the metafield definition was created and was never updated.
mf.valueis blank, the block renders empty, and it looks broken, but the system is working as configured.Fix: in admin → Products → pick a broken product → scroll to Metafields → confirm the field has a value. If empty, fill it. For bulk fills, use the bulk editor (Products → Select all → Bulk edit → add the metafield column).
- 04Product uses an alternate template.
The metafield block was added in the customiser to
product.json, but the affected products are assigned toproduct.alternate.json(or any other custom template). The block exists on the wrong template and never renders for those products.Fix: in admin → Products → broken product →Theme template dropdown shows which template is assigned. Either reassign to the default template, or open the alternate template in the customiser and add the metafield block there too.
- 05App-managed metafield with private namespace.
Metafields set by an installed app live under a namespace like
app--12345678--reviews. These are not directly readable in Liquid unless the app exposes them or you use the app's own block. Reading them with a hard-coded reference returns empty.Fix: use the app's own theme block or embed instead of reading the metafield directly. If you need the data in custom Liquid, contact the app developer for a public reference path or replicate the data in your own metafield definition.
The type-aware renderer (cause #2)
Pass any metafield to this snippet and it picks the right output: metafield_tag for rich text, newline_to_br for multi-line, image tags for file references, links for product/page/collection references, loops for lists.
{% comment %}
Jelonyx · Type-aware metafield renderer
Renders a metafield correctly regardless of type: string, rich text, list,
product reference, file reference, etc.
Use: {% render 'jlx-metafield', mf: product.metafields.custom.spec %}
Source: jelonyx.com/shopify/fix/metafields-not-showing
{% endcomment %}
{%- liquid
if mf == blank or mf.value == blank
break
endif
assign t = mf.type
-%}
{%- case t -%}
{%- when 'rich_text_field' -%}
{{ mf | metafield_tag }}
{%- when 'multi_line_text_field' -%}
{{ mf.value | newline_to_br }}
{%- when 'single_line_text_field', 'number_integer', 'number_decimal', 'date', 'date_time', 'url', 'json_string' -%}
{{ mf.value }}
{%- when 'boolean' -%}
{{ mf.value | default: false }}
{%- when 'product_reference' -%}
<a href="{{ mf.value.url }}">{{ mf.value.title }}</a>
{%- when 'file_reference' -%}
{%- if mf.value.media_type == 'image' or mf.value.image -%}
{{ mf.value | image_url: width: 800 | image_tag: loading: 'lazy', alt: product.title }}
{%- else -%}
<a href="{{ mf.value.url }}" download>{{ mf.value.url | split: '/' | last }}</a>
{%- endif -%}
{%- when 'page_reference', 'collection_reference' -%}
<a href="{{ mf.value.url }}">{{ mf.value.title }}</a>
{%- when 'metaobject_reference' -%}
{%- for field_pair in mf.value -%}
<div class="mf-row">
<span class="mf-label">{{ field_pair[0] }}:</span>
<span class="mf-val">{{ field_pair[1] }}</span>
</div>
{%- endfor -%}
{%- else -%}
{%- comment -%} Lists are 'list.<inner_type>': handle with same logic per item {%- endcomment -%}
{%- if t contains 'list.' -%}
<ul class="mf-list">
{%- for item in mf.value -%}
<li>
{%- if item.url and item.title -%}
<a href="{{ item.url }}">{{ item.title }}</a>
{%- elsif item.image -%}
{{ item | image_url: width: 200 | image_tag: loading: 'lazy' }}
{%- else -%}
{{ item }}
{%- endif -%}
</li>
{%- endfor -%}
</ul>
{%- else -%}
{{ mf.value }}
{%- endif -%}
{%- endcase -%}Use anywhere on the product page:
{% render 'jlx-metafield', mf: product.metafields.custom.spec %}When to call a developer
- Metaobject references with deep nesting: e.g. a product references a metaobject which references another metaobject. The renderer covers one level; deeper structures need a custom loop.
- You need the metafield in the cart or checkout:that's a cart attribute or line-item property pattern, not a product metafield. Different fix.
- Metafield content needs SEO structured data:wrapping it in JSON-LD requires understanding the schema vocabulary for your product category.
- Headless storefront (Hydrogen, custom React): the storefront API needs the definition to have Storefront access enabled in the metafield settings, plus a different query pattern.
Need this fixed today?
Metafields power product specs, ingredient tables, downloadable files, and review snippets. When they don't render, the product page reads as incomplete. We diagnose the exact namespace and type mismatch, install the right renderer, and verify across every product template.