jelonyx
§ / Shopify · Error fixes · Fix 03

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.

SeverityContent-blocking
Time to fix~10–30 minutes
Theme accessRequired
Most common causeWrong namespace
Affects:Dawn 12+HorizonAll OS 2.0 themesCustom themes

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.

snippets/jlx-metafield-debug.liquid
Liquid
{% 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_field or starts with list. → 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

  1. 01
    Wrong namespace or key in the Liquid reference.

    The admin labels metafields with a friendly name like Product spec, but Liquid needs the exact namespace.key pair from the definition. A developer who guessed custom.spec when the definition is actually specs.product gets 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.

  2. 02
    Structured 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.title for products, mf.value | image_url for 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.

  3. 03
    Specific 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.value is 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).

  4. 04
    Product uses an alternate template.

    The metafield block was added in the customiser to product.json, but the affected products are assigned to product.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.

  5. 05
    App-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.

snippets/jlx-metafield.liquid
Liquid
{% 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.
No-App Shopify Fix Sprint

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.