Every Shopify theme developer eventually faces a fork in the road: "Should I store this data in a metafield, or build a metaobject definition?" The wrong choice becomes obvious six months later when a merchant wants to update a shared piece of content and has to edit dozens of individual product records instead of one place. This guide cuts through the confusion with a feature-by-feature comparison and clear guidance on which structure to choose.
| Feature | Metafields Typed key-value data attached to a specific Shopify resource (product, variant, collection, customer, etc.) | Metaobjects Reusable, structured content entries defined by a custom schema, referenced from resources via metafield references |
|---|---|---|
| Data shape | Single typed value per namespace.key (e.g. single_line_text_field, list.product_reference, rich_text_field) | Multiple typed fields per entry, structured like a mini content type |
| Owning resource | Always belongs to one resource (product, variant, collection, customer, order, shop, location, blog, article, page) | Standalone entries — referenced from any resource via a metaobject_reference metafield |
| Relational references | Can reference products, collections, pages, articles, variants via list.product_reference etc. | Can reference other metaobjects, creating a graph of content relationships |
| Reusability | Not reusable — each resource has its own copy of the value | Fully reusable — one Author entry can be linked from hundreds of products |
| Liquid access pattern | product.metafields.custom.key.value — direct access on the owning object | product.metafields.custom.author.value.name — via a .value resolution step |
| Admin UX | Appears in the Metafields section at the bottom of each resource edit page | Managed in Content > Metaobjects — separate from individual resource pages |
| API surface | Admin REST: /metafields.json; GraphQL: metafieldsSet mutation; Storefront API: product.metafields | Admin GraphQL: metaobjectCreate, metaobjectUpdate; Storefront API: metaobjects connection |
| Dynamic sources in theme editor | Can be bound to section/block settings of matching type | Individual fields can be bound if the metaobject reference is resolved first |
| Migration cost | Low — definition creation + bulk data entry via CSV or import app | Higher — requires definition, entry creation, then linking resources (metafield assignments) |
| When introduced | Metafields have existed since early Shopify; pinned definitions added in 2021 with OS 2.0 | Metaobjects introduced at Shopify Editions Summer 2022 |
If the data is unique to one product (a product-specific weight, a custom disclaimer, a per-variant colour hex code), use a metafield. If the same structured content needs to appear on multiple products — or if a merchant would naturally think of it as a separate record to manage — use a metaobject.
When to pick metafields
Reach for a metafield when the data is intrinsically bound to one resource and varies independently for every instance. A product's custom short description, a variant's EAN barcode, a collection's SEO lead paragraph, or a customer's loyalty tier — these are all values that belong to exactly one record and have no life outside of it. A metafield with namespace custom and key short_description of type single_line_text_field is the right model here.
Metafields are also the right choice when you need to associate another Shopify resource with a product: a "related bundle" product reference (product_reference type), a curated list of up-sell products (list.product_reference type), or a linked collection. These are native Shopify object references — no metaobject needed.
Metafields also have a lower setup burden. A single definition in Settings > Custom data, a CSV bulk import, and you're done. If you're migrating a headless implementation or importing data from a PIM system, the simplicity of a flat metafield is an asset.
When to pick metaobjects
Metaobjects are the right choice when the data is reusable across multiple resources, has multiple fields of its own, and would be naturally managed as a standalone record. Author bios, brand profiles, certifications, events, size guide tables, ingredient definitions — these are all entities that exist independently of any single product and would be a maintenance nightmare if duplicated as flat metafields on every product.
The maintenance argument is decisive. Imagine a supplement brand with 200 products, each authored by one of five nutritionists. If those authors are metafields on each product, updating a headshot means editing 200 product records. If they're metaobjects, you update one Author entry and all 200 products reflect the change instantly. This is the canonical reason to prefer metaobjects for shared content.
Metaobjects also scale into relational data structures. A "Recipe" metaobject can reference multiple "Ingredient" metaobjects, each of which references a product. This kind of graph is impossible with flat metafields — you'd need nested JSON hacks to approximate it, and there's no admin UX for maintaining that structure. Metaobjects give you that graph natively, with type safety enforced at the definition level.
Real-world examples
Use metafields for:
- Product: a washing instructions field (type:
rich_text_field) — unique to each garment product - Variant: a colour hex value (type:
color) for rendering colour swatches without an app - Collection: a hero subtitle paragraph (type:
single_line_text_field) shown above the product grid - Product: a curated cross-sell list (type:
list.product_reference) — editorial, per-product selection
Use metaobjects for:
- Author — shared bio cards referenced from blog articles and research-backed products
- Brand — logo, description, founding story, linked to all products from that brand
- Certification — organic, cruelty-free badges (name, icon, issuing body) linked to multiple products
- Size guide — measurements table shared across an entire clothing line, not duplicated per product
- Ingredient — definition, safety notes, sourcing region; used in recipe pages and on relevant product pages simultaneously
Liquid access patterns side by side
The Liquid syntax for accessing the two structures differs in a meaningful way that catches developers off guard the first time. A standard metafield is accessed directly on the owning resource: product.metafields.custom.short_description.value returns the raw typed value — a string for single_line_text_field, an integer for number_integer, or a product object for product_reference.
A metaobject reference is accessed the same way — product.metafields.custom.author — but .value on that expression returns the resolved metaobject entry. From there you navigate its fields using the field key names defined in the metaobject definition: product.metafields.custom.author.value.name, product.metafields.custom.author.value.bio. If you forget the .value step and try to access product.metafields.custom.author.name directly, Liquid returns nil — because name is not a property of the metafield object itself, it's a property of the metaobject entry that .value resolves to. This is the single most common mistake when working with metaobject references in Liquid.
For list-type metafields (e.g. list.product_reference or list.metaobject_reference), .value returns an array. You iterate it with a for loop: for cert in product.metafields.custom.certifications.value. Each cert in the loop is then a fully resolved metaobject entry with dot-access to its fields.
Migration considerations
If you've already built a solution with plain metafields and later realise it should have been a metaobject, migration is possible but manual. There is no built-in Shopify tool to convert a set of flat product metafields into a metaobject definition and re-link the products. The migration path typically involves: creating the metaobject definition, using the Admin GraphQL API to bulk-create metaobject entries from the existing metafield data, then updating each product's new metaobject reference metafield. For a catalogue of 50 products this is manageable with a one-off script; for a 5,000-product store it requires careful bulk API work.
The reverse migration — from metaobject back to plain metafields — is rarely needed and even more rarely the right call. If you find yourself wanting to "flatten" a metaobject, it's usually a sign that the data model was more complex than the problem actually required. Resist the temptation to over-engineer early. Start with a metafield when the requirements are unclear and migrate to a metaobject definition once the need for shared, structured data becomes undeniable.
API surface and headless considerations
If you're building a headless storefront with the Shopify Storefront API, both metafields and metaobjects are queryable. Metafields are accessed on their owner resource: product.metafields(identifiers: [{namespace: "custom", key: "badge_label"}]). Metaobjects are queryable via the top-level metaobjects(type: "author") connection. For headless storefronts, metaobjects are often preferable for global, reusable content because you can fetch all instances of a type in a single query rather than fetching each product individually to collect its metafield value.
One practical difference in the Storefront API: metafield values on a product are only returned if the definition has been pinned (created through Settings > Custom data) and the "Storefront" access is enabled. Metafields created programmatically through the Admin API without a pinned definition will not be accessible via the Storefront API. This is a common gotcha when connecting a third-party integration that creates metafields programmatically — verify storefront access for each definition before building the frontend query.
