How to Display Shopify Metafields in a Section

Render product and collection metafields in a storefront section: define the field, read it in Liquid, handle each metafield type, and guard for empty values.

5 min readUpdated

Part of our guide to Shopify Metafields: Display Them in Your Storefront.

This guide takes a metafield from "defined in the admin" to "showing on the storefront" inside a custom section. You will build a Product details section that renders a product's material, a care guide, and a downloadable spec sheet — each read from its own metafield, each hidden when the product has no value for it. It assumes an Online Store 2.0 theme and that you can edit theme code.

If you are still deciding whether a metafield is the right home for this data at all, start with the Shopify Metafields pillar; it covers types, namespaces, and when a metafield beats a plain theme setting.

Step 1 — Define the metafields you will read

You cannot render a metafield the theme cannot see. In the admin go to Settings → Custom data → Products → Add definition and create three definitions, making sure each has storefront access enabled:

  • custom.material — type Single line text
  • custom.care_guide — type Rich text
  • custom.spec_sheet — type File

Then open a product and fill those fields in under Metafields at the bottom of its page. A section can only render what a product actually has, so give at least one product real values to test against.

Step 2 — Create the section file

Add a section file at sections/product-details.liquid. Because this section reads from product, it belongs on the product template — add it there in the theme editor once the file exists.

Start with the wrapper and the simplest field, the single-line text material:

<section class="product-details">
  <div class="product-details__inner">
    {% if section.settings.heading != blank %}
      <h2 class="product-details__heading">{{ section.settings.heading }}</h2>
    {% endif %}

    {% if product.metafields.custom.material != blank %}
      <p class="product-details__row">
        <span class="product-details__label">Material:</span>
        {{ product.metafields.custom.material }}
      </p>
    {% endif %}
  </div>
</section>

The != blank guard is doing real work here. Metafields are optional per product, so without the guard, products with no material would render a stray "Material:" label with nothing after it.

Step 3 — Render a rich-text metafield

A rich_text_field does not output as plain text — it holds structured rich text. Piping it through the metafield_tag filter renders it as proper HTML (paragraphs, links, lists) instead of escaped markup:

{% if product.metafields.custom.care_guide != blank %}
  <div class="product-details__care">
    <h3>Care</h3>
    {{ product.metafields.custom.care_guide | metafield_tag }}
  </div>
{% endif %}

If you output a rich-text metafield with just {{ … }}, older behaviour renders the value but you lose control over the wrapping markup; metafield_tag is the current, predictable way to render it. The full list of which filter suits which type is in Metafield tokens and filters.

Step 4 — Render a file metafield

A file_reference metafield gives you a file object, not a URL string. To build a download link you read the file's url and, for a nicer label, its filename:

{% if product.metafields.custom.spec_sheet != blank %}
  {% assign spec = product.metafields.custom.spec_sheet.value %}
  <p class="product-details__download">
    <a href="{{ spec.url }}" download>
      Download spec sheet
    </a>
  </p>
{% endif %}

Note the .value — for typed metafields, .value gives you the underlying object (here, the file) so you can reach its properties. If the file is an image you can also pass it through image filters like image_url and image_tag.

Step 5 — Add the schema

Give the section a name, one editable heading, and a preset so it appears in the editor's "Add section" menu. Add this at the bottom of the file:

{% schema %}
{
  "name": "Product details",
  "tag": "section",
  "settings": [
    {
      "type": "text",
      "id": "heading",
      "label": "Heading",
      "default": "Product details"
    }
  ],
  "presets": [
    { "name": "Product details" }
  ]
}
{% endschema %}

The metafield keys are hard-coded in the Liquid here, which is fine for a fixed layout. If you want a merchant to choose which metafield the section shows from the editor — without editing code for each new field — expose it as a token; that pattern is covered in Metafield tokens and filters.

Step 6 — Add the section and verify

Save the file. In Online Store → Themes → Customize, open a product template, click Add section, and add Product details. Preview a product that has the metafields filled in — you should see the material, the care guide, and the download link — and a product that does not, where the whole block (or each empty row) simply does not render.

Metafields on other resources

The same pattern works beyond products. Swap the object in front of .metafields:

  • Collection: collection.metafields.custom.intro in a collection section.
  • Page: page.metafields.custom.disclaimer in a page template.
  • Shop-wide: shop.metafields.custom.hours for a value that is the same store-wide.

The type-to-filter rules are identical regardless of the resource — only the object in front changes.

Keeping this section alive across theme updates

This section now lives in sections/product-details.liquid — a theme file. The metafield data is safe (it lives on the products), but the file that renders it is part of this theme version. Install a new version of the theme, or switch themes, and Shopify publishes a fresh copy of the theme's files; your product-details.liquid is not carried over, and the block disappears even though the data behind it is untouched.

Two ways to protect it: keep a disciplined re-apply workflow after every theme update (the method is here), or render the section from an app via a Theme App Extension so no update or swap can touch it. If this is a section you rely on, SectionGuard renders metafield-driven blocks like this from the app — reading your existing metafields, surviving every theme change.