Skip to content

Metafields

Metafields hold content that belongs to a product, variant, collection, or page rather than to a section — data that should follow the record wherever it’s rendered. If content only ever appears in one section, it should be a section setting or a block, not a metafield.

Namespace Owner Status
migrelief This build Use this. 31 definitions across product, variant, collection, page
custom Previous agency Legacy — 30 product definitions, being retired
custom_fields Previous agency Legacy — no definitions in the store, read by four blocks
descriptors, seo, shopify, shopify--discovery--* Shopify Never touch. Shopify-reserved and app-managed
stamped, okendo, reviews, smile, mm-google-shopping, mm_google_shopping_extension Apps Never touch. Renaming breaks the integration

A namespace change is not a rename. Shopify has no rename operation — moving a key to migrelief means creating a new definition and copying every stored value across. Plan legacy migrations as data work, not a find-and-replace.

Definitions live in the store, not the repo. Refresh the local snapshot with:

Terminal window
shopify theme metafields pull --store=migrelief

That writes .shopify/metafields.json, which is git-ignored — it’s a cache, not source of truth. It lists definitions only. A metafield can hold values on every product without having a definition at all, so this file cannot tell you whether data exists — only whether the field is declared and visible in admin.

Two of these are read by hardcoded Liquid; the rest are either bound through the theme editor or not yet wired.

Key Type Purpose Read by
product_short_description multi_line_text_field Short product description templates/product.json (richtext block)
caplet_override single_line_text_field Overrides the word “Caplet” in the price-per-unit selector product-pack-selector, cart
automatic_discount_code single_line_text_field Added to carts on add. Must exactly match an active discount code
automatic_discount_message single_line_text_field Cart message for automatic discounts
benefits multi_line_text_field Short benefit list, one per line
cart_description single_line_text_field Subtitle under the cart title
cart_display_name single_line_text_field Overrides the product title in cart
cart_display_price number_decimal Strikethrough price in cart upsell
compare_price number_decimal Overrides compare price, for auto-discounts
product_price number_decimal Overrides normal price, for auto-discounts
enable_price_override_for_subscription_product boolean Show metafield prices in cart for subscriptions
show_price_overrides_in_cart boolean Show metafield prices in cart for one-time purchases
excerpt multi_line_text_field Short excerpt, used across the store
limit_one_purchase boolean
navigation_title single_line_text_field Overrides product title in the Shop megamenu
navigation_description multi_line_text_field Overrides product excerpt in the Shop megamenu
navigation_thumbnail file_reference Image in the Shop megamenu
noindex_product boolean
product_featured_information_heading single_line_text_field
product_featured_information_content multi_line_text_field
product_image_background color
product_url url
Owner Key Type Purpose Read by
Variant product_supply number_integer How long the variant supplies for, e.g. 30-day product-pairs-with, product-price-display, cart, product-card-wide
Variant total_quantity number_integer Caplets/capsules in the pack product-pack-selector, cart
Variant compare_price number_decimal Overrides variant compare price for auto-discounts
Variant product_price number_decimal Overrides variant price for auto-discounts
Variant gallery_images list.file_reference Replaces the default gallery entirely when set
Collection collection_page_title single_line_text_field Overrides collection title on v2 templates
Collection collection_page_description multi_line_text_field Overrides collection description
Page noindex_page boolean
Page reviews list.mixed_reference

Keys the theme reads that have no definition

Section titled “Keys the theme reads that have no definition”

These work — Liquid reads unstructured metafields fine — but they don’t appear in admin as managed fields, so merchants can’t edit them through the normal UI and the values are invisible to anyone auditing the store.

Key Read by Notes
custom.species product-variant-options, main-product
custom.hero_title hero-collection Collection-scoped. Falls back to collection.title
custom.hero_description hero-collection, meta-tags Collection-scoped. Falls back to collection.description
custom.hero_color_scheme hero-collection Collection-scoped. Falls back to 'Dark'
custom.related_articles related-articles
custom.related_pages related-articles
custom.short_description meta-tags
custom.product_card_image_override product-card-wide Preferred over product.featured_image on the wide card
custom_fields.accordion accordion, product-accordion
custom_fields.sale_description collection-sale-description, product-sale-description
custom_fields.show_sale_description collection-sale-description
custom_fields.show_product_sale_description product-sale-description
okendo.summaryData product-rating App-written — expected to be undefined

stamped.reviews_average, stamped.reviews_count, descriptors.subtitle and seo.canonical_url are also read without definitions. That’s normal for app-owned and Shopify-reserved namespaces.

Every one of these reads is defensively coded with a default: filter or a blank guard, so a missing value degrades rather than breaking the page. That’s the pattern to follow when consuming any metafield.

Four sections in the old theme (akeso-shopify-theme) drove PDP content entirely from custom metafields, on templates/product.custom.json and templates/product.v3.json. None are used by this build.

Metafields Old section
image_with_text_title, _description, _image, and the 2 variants image-with-text.liquid
card_video_or_image, _title, _description video-cards-pdp.liquid
image_hover_text, _title section--feature-cards-pdp.liquid
video_with_text_video, _title, _description Same pattern

From an Admin API pull on 6 August 2026 (57 products). The blast radius is small — five products, three of which are TTS duplicates or a test product.

Keys Products
image_hover_text*, card_video_or_image* MigreLief Original Formula (migrelief-original-formula-60-ct-30-day-supply), MigreLief Original Formula TTS (migrelief-original-formula-tts), Test Not For Sale (test-not-for-sale)
image_with_text_*2 MigreLief Migraine & Headache Relief Cap (migraine-headache-relief-cap), and its TTS twin (migrelief-migraine-headache-relief-cap-tts)
image_with_text_* (base), video_with_text_video 19 products, all on the v3 template

Test Not For Sale is the only product on templates/product.custom.json and carries the full set — useful for seeing all three old sections at once.

Worth understanding before rebuilding, because the pattern is invisible from the customizer. The sections are empty shells: they read metafields straight off the product and their schema settings (title, content, container_width) are declared but never referenced in the markup. Editing them in the theme editor does nothing.

For content to appear, two unrelated things must both be true — the product must be assigned a template that includes the section (product.v3 or product.custom), and the metafield must be non-blank. Miss either and the section renders nothing, silently.

Three of those — card_video_or_image, image_hover_text, and video_with_text_video — are multi_line_text_field holding a delimited pseudo-array: || separates cards, ++ separates fields within a card.

A real value from image_hover_text on Original Formula:

feverfew-1200×667.webp++Puracol® Feverfew++Encourages a healthy inflammatory response...||
iStock-1055964066.jpg++Magnesium++Supports muscle relaxation, nerve function & brain health.||

[0] filename, [1] heading, [2] body. video-cards-pdp.liquid additionally sniffs the extension — .mp4/.webm render as a lazy-loaded muted autoplay <video> from /cdn/shop/videos/c/vp/, anything else as an <img> from /cdn/shop/files/.

{%- assign card_items = product.metafields.custom.card_video_or_image | split: '||' -%}
{%- for item in card_items -%}
{%- assign card_data = item | split: '++' -%}

The filenames in card_data[0] were concatenated onto a hardcoded absolute CDN path (https://store.migrelief.com/cdn/shop/files/), so they got no image_url transforms, no srcset, and no dimensions.

Not all of custom is disposable. Four keys are live right now, bound as dynamic sources in templates/product.json accordion items:

Key Rendered as
custom.product_information “Product Details” accordion item
custom.ingredients “Ingredients” accordion item
custom.recommended_use “Recommended Use” accordion item
custom.references “References” accordion item

Each also exists as a _html twin (ingredients_html etc.) typed single_line_text_field. The theme uses the rich_text_field version; the _html copies appear unused.

Also potentially worth migrating rather than dropping: ingredient_metaobjects, faq_cluster, compare_product / _description, as_featured_in, sub_title, pdp_faqs_heading_1 / _description_1.

  1. Check it isn’t a section setting. Metafields are for record-scoped data reused across templates. One-section content belongs in a block.
  2. Create it in migrelief, on the narrowest owner that fits — variant over product when the value genuinely varies by variant.
  3. Write the description. It’s the only guidance a merchant sees in admin. Say what overrides what, and what happens when it’s empty.
  4. Guard the read with default: or a blank check. Never assume a value.
  5. Document it here — add the row, and note what reads it.
  • Whether the undefined custom.* and custom_fields.* keys hold real values is unresolved. Definitions alone can’t answer it; it needs a value-level query against the store — the same Admin API pull that produced the product list above, widened to those keys.
  • Most migrelief definitions show no reader. Some are built-not-yet-wired, some may be stale. Each needs a decision before launch.
  • No migration plan yet for the legacy custom keys that survive.