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.
Namespaces
Section titled “Namespaces”| 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.
Reading the inventory
Section titled “Reading the inventory”Definitions live in the store, not the repo. Refresh the local snapshot with:
shopify theme metafields pull --store=migreliefThat 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.
migrelief — product
Section titled “migrelief — product”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 | — | — |
migrelief — variant, collection, page
Section titled “migrelief — variant, collection, page”| 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.
Legacy custom — the PDP content set
Section titled “Legacy custom — the PDP content set”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 |
Which products actually carry them
Section titled “Which products actually carry them”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.
How the old sections consumed them
Section titled “How the old sections consumed them”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.
Legacy custom still worth keeping
Section titled “Legacy custom still worth keeping”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.
Adding a metafield
Section titled “Adding a metafield”- Check it isn’t a section setting. Metafields are for record-scoped data reused across templates. One-section content belongs in a block.
- Create it in
migrelief, on the narrowest owner that fits — variant over product when the value genuinely varies by variant. - Write the description. It’s the only guidance a merchant sees in admin. Say what overrides what, and what happens when it’s empty.
- Guard the read with
default:or a blank check. Never assume a value. - Document it here — add the row, and note what reads it.
Known gaps
Section titled “Known gaps”- Whether the undefined
custom.*andcustom_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
migreliefdefinitions 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
customkeys that survive.