Shopify metafields are structured custom fields attached to products, variants, collections, customers and orders. Define them in Settings → Custom data, populate them via the admin, CSV, Admin API or apps, and render them on the storefront via {{ product.metafields.namespace.key }} in Liquid. Metaobjects are reusable multi-field records — the modern replacement for hardcoded theme content.
- Always define metafields explicitly in Settings → Custom data before writing to them.
- Standard metafields (e.g. `descriptors.subtitle`) enable native theme integrations.
- Metaobjects unlock reusable content blocks that non-devs can edit.
- Metafield type mismatches silently corrupt data — validate before bulk imports.
- GraphQL is the only supported API for JSON and reference-type metafields.
Metafields = structured custom data on any resource. Metaobjects = reusable records referenced by metafields. Together they replace 80% of theme customizations and app dependencies.
What metafields actually are
A metafield is a typed key–value pair attached to a Shopify resource — a product, variant, collection, customer, order, blog, article, page, market or shop. Unlike a tag (a single unstructured string) a metafield has a namespace, a key, a validated type, and can hold structured or referenced data. When you define a metafield in Settings → Custom data, Shopify enforces the type at write time — try to write text into a `number_decimal` metafield and the mutation fails cleanly instead of corrupting your data. This is the difference between a professional Shopify catalog and a fragile one.
The type system
Types are not cosmetic — they change what the admin editor looks like, what the storefront receives, and which apps can consume the field. Pick the narrowest type that fits the data.
| Type | Best for | Storefront output |
|---|---|---|
| single_line_text_field | SKU codes, short labels | String |
| multi_line_text_field | Long descriptions, care instructions | String with newlines |
| rich_text_field | Formatted marketing copy | JSON — render via `metafield_tag` |
| number_integer / number_decimal | Quantities, weights, ratings | Number |
| boolean | Toggle flags (`hasWarranty`) | true / false |
| file_reference | PDFs, spec sheets, videos | File object with URL |
| product_reference | 'Frequently bought together' | Product object |
| metaobject_reference | Reusable content (authors, sizing charts) | Metaobject with fields |
| json | Complex nested data | Parsed object |
Defining before writing
Never populate an undefined metafield. Shopify accepts it, but the admin won't expose it in editors, the storefront can't type-check it, and CSV imports will silently create duplicates under slightly different namespaces. Always start in Settings → Custom data → Add definition. Pick a namespace that matches the domain (`specs`, `care`, `seo`) — avoid the default `custom` bucket because that's where every third-party app dumps its own data.
Every app that writes metafields defaults to `custom.*`. If you follow suit your store's data becomes indistinguishable from third-party garbage. Reserve a short namespace like `abar.` or `shop.` for your own definitions.
Rendering on the storefront
Once defined and populated, metafields become available in Liquid at `{{ product.metafields.namespace.key }}`. For rich text and metaobject references, use the built-in filters and object accessors — never try to render them as raw strings.
{% comment %} Simple text {% endcomment %}
<p>{{ product.metafields.specs.material }}</p>
{% comment %} Rich text — must use metafield tag {% endcomment %}
{{ product.metafields.marketing.pitch | metafield_tag }}
{% comment %} Metaobject reference — access fields {% endcomment %}
{% assign chart = product.metafields.abar.size_chart.value %}
<h3>{{ chart.title }}</h3>
{{ chart.body | metafield_tag }}
{% comment %} File reference — resolve to URL {% endcomment %}
<a href="{{ product.metafields.docs.spec_sheet.value.url }}">
Download spec sheet
</a>Bulk-editing metafields
The native bulk editor gained metafield support in 2024, but with caveats: only simple types (text, number, boolean) work reliably, and you must add each metafield as a column manually. For JSON, references or media metafields, CSV is the only reliable admin path — and CSV rules are strict.
- The column header must be exactly `Metafield: namespace.key [type]` (case-sensitive).
- The `type` in brackets must match the definition; a mismatch silently skips the row.
- For reference types, the cell value is a `gid://shopify/Product/12345` global ID — not a handle.
- For JSON, the cell must be minified JSON on a single line, no linebreaks.
- Empty cells clear the field; whitespace-only cells write literal spaces.
If the referenced product is deleted or its ID changes, the CSV import succeeds but the metafield returns null on the storefront. Always re-export and diff after an import touching references.
The Admin API path (GraphQL)
REST metafields are legacy — use GraphQL for anything new. The `productUpdate` mutation accepts a `metafields` array where each item is namespace, key, type and value. For JSON metafields the value is a stringified JSON. For reference types the value is the global ID of the referenced resource.
mutation UpdateProductSpecs($id: ID!) {
productUpdate(input: {
id: $id
metafields: [{
namespace: "specs"
key: "material"
type: "single_line_text_field"
value: "Organic cotton"
}]
}) {
product { id }
userErrors { field message }
}
}Metaobjects: the reusable layer
A metaobject is a typed record with multiple fields — think of it as a lightweight database table inside Shopify. Define a metaobject type (`author`, `size_chart`, `feature_block`), populate instances in the admin, then reference them from products or the theme via metafields. Metaobjects power the modern 'headless-lite' pattern: content teams edit metaobjects, developers reference them in Liquid, and the site stays fast because everything ships in the Shopify response — no external CMS round-trip.
Common metafield patterns
These are the highest-leverage metafield definitions we deploy for merchants.
- `specs.material`, `specs.weight`, `specs.dimensions` — structured product attributes that unlock filtered navigation and Google Shopping attributes.
- `docs.spec_sheet` — file reference for PDFs, shown as a native download button.
- `marketing.pitch` — rich text for the copy that lives above the fold, editable without touching Liquid.
- `abar.related_products` — product reference list for cross-sell blocks that respect merchandiser intent instead of algorithmic guesses.
- `abar.badges` — metaobject reference for reusable trust badges (Made in EU, Vegan, Certified) that render consistently across every product.
Performance and limits
There is no hard cap on metafields per resource, but the storefront GraphQL query for a product returns metafields lazily — every field you access adds to page render time. Keep the top-of-product-page metafields under ~10, and lazy-load spec sheets and long-form content into tabs or accordions. For collection pages, expose only the metafields you actually use in cards; querying every metafield 'just in case' is the #1 cause of slow collection pages on otherwise-fast themes.
Debugging metafields that don't render
When a metafield is populated in the admin but blank on the storefront, walk this checklist before blaming the theme.
- 1Verify the definition
Settings → Custom data → your definition exists with the exact namespace and key your Liquid references.
- 2Confirm the type
Rich text renders differently from text — `{{ … }}` outputs `[object Object]` for rich text. Use `| metafield_tag`.
- 3Check publication scope
Some metafield definitions default to hidden from the Storefront API. Toggle 'Storefronts' access in the definition.
- 4Rebuild theme cache
Themes cache metafield definitions. After a definition change, save the theme once to trigger a rebuild.
- 5Query via GraphiQL
If Storefront API returns null, the issue is data. If it returns the value, the issue is Liquid.
Frequently asked questions
The most common questions merchants ask us about data modeling.