← Files Shopify App BuilderARCHIVED FILE
skills/metafields-metaobjects/SKILL.md
17.6 KB · Oct 5, 2026 · 18:30 UTC
---
name: metafields-metaobjects
description: "Custom data fields and objects specification, namespace/key management, definition creation, querying, and Liquid theme access. Triggers include: 'metafield', 'metaobject', 'custom field', 'custom data', 'namespace key', 'metafield definition', 'metaobject type', 'product metafield', 'variant metafield', 'customer custom field', 'order metafield'."
---
# Shopify Metafields and Metaobjects Guide
## Metafields vs Metaobjects: When to Use Each
### Metafields
**Purpose:** Store custom key-value data on existing resources (products, orders, customers, etc.)
**Use Metafields When:**
- Adding custom attributes to existing resources
- Simple key-value relationships
- Data is tightly coupled to the resource
- Needed in themes (Liquid access)
- Examples: size chart URL, custom color, warranty period, gift message
**Characteristics:**
- Attached to existing resource types
- Simple string or typed values
- Queryable via GraphQL
- Accessible in Liquid templates
- Max 2,500 metafields per resource
- Namespace + key = unique identifier
- Supports display in admin UI via definitions
### Metaobjects
**Purpose:** Define custom data types/records independent of resources
**Use Metaobjects When:**
- Creating independent data structures
- Multi-field records needed
- Reusable data types across shop
- Complex relationships
- Data referenced by multiple resources
- Examples: FAQs, reviews, testimonials, size guides, lookbooks, staff profiles
**Characteristics:**
- Independent data type (like custom table)
- Multiple fields with defined types
- Queryable via GraphQL
- Can be referenced by products/collections via metafield
- Organized in admin UI
- Supports indexing and search
- Better for data that stands alone
**Comparison Table:**
| Feature | Metafield | Metaobject |
|---------|-----------|-----------|
| Attached to resources | Yes | No (standalone) |
| Multi-field support | No (single value) | Yes |
| Admin UI form | Via definition | Native editor |
| Liquid access | Direct | Via reference metafield |
| Relationship support | One-way (to resource) | One-way (reference metafield) |
| Reusability | Per resource type | Across shop |
| Use case | Quick attributes | Structured data |
## Metafield Resource Types
The following resources support metafields:
1. **Product** - 2,500 max metafields per product
2. **Product Variant** - 2,500 max metafields
3. **Order** - Custom order data
4. **Customer** - Customer profiles
5. **Collection** - Collection attributes
6. **Draft Order** - Pre-order metadata
7. **Shop** - Store-wide settings
8. **Location** - Warehouse/store details
9. **Company** - B2B company info
10. **Company Location** - B2B location data
11. **Market** - Market-specific metadata
12. **File** - Asset metadata
## Metafield Type System
### Supported Types
**Text Types:**
- `single_line_text_field` - Max 255 chars (search enabled, sortable)
- `multi_line_text_field` - Max 5,000 chars (search enabled)
- `rich_text_field` - HTML + Markdown (max 100,000 chars)
**Numeric Types:**
- `number_integer` - 64-bit signed integer
- `number_decimal` - Decimal number (up to 8 decimal places)
**Date/Time Types:**
- `date_field` - ISO 8601 date (YYYY-MM-DD)
- `date_time_field` - ISO 8601 datetime (2026-05-04T14:30:00Z)
**Boolean:**
- `boolean` - true/false
**Reference Types:**
- `product_reference` - Reference to Shopify product
- `collection_reference` - Reference to collection
- `file_reference` - Reference to file in Files API
- `metaobject_reference` - Reference to metaobject
**Display Types:**
- `color` - Color value (hex #RRGGBB)
- `weight` - Weight with unit (grams, kilograms, pounds, ounces)
- `volume` - Volume with unit
- `dimension` - Dimension with unit
- `url` - Full URL (max 2,048 chars)
**JSON Type:**
- `json` - Valid JSON object/array (max 100,000 chars, searchable)
**List Type:**
- `list.*` - Array of type (e.g., `list.metaobject_reference`)
### Type Examples
**Single Line Text:**
```graphql
{
namespace: "app",
key: "manufacturer_id",
type: "single_line_text_field",
value: "MFG-12345"
}
```
**JSON for Complex Structure:**
```graphql
{
namespace: "app",
key: "compatibility_matrix",
type: "json",
value: "{\"platforms\": [\"iOS\", \"Android\"], \"min_version\": \"12.0\"}"
}
```
**Weight:**
```graphql
{
namespace: "app",
key: "shipping_weight",
type: "weight",
value: "{\"value\": 2.5, \"unit\": \"kg\"}"
}
```
**Product Reference:**
```graphql
{
namespace: "app",
key: "replacement_product",
type: "product_reference",
value: "gid://shopify/Product/123456789"
}
```
**List of Metaobject References:**
```graphql
{
namespace: "app",
key: "related_guides",
type: "list.metaobject_reference",
value: "[\"gid://shopify/Metaobject/12345\", \"gid://shopify/Metaobject/67890\"]"
}
```
## Namespace and Key Conventions
### Namespace Rules
- Unique to your app/vendor
- Used to organize related metafields
- Immutable once set
- Best practice: use `app` or your app handle
- Example namespaces: `app`, `seo`, `loyalty`, `inventory_tracking`
### Key Rules
- Lowercase alphanumeric + underscores
- Max 64 characters
- Immutable once set
- Should be descriptive
- Examples: `custom_size`, `warranty_months`, `supplier_sku`
### Naming Convention Table
| Use Case | Namespace | Key | Full Name |
|----------|-----------|-----|-----------|
| App custom fields | `app` | `custom_color` | app.custom_color |
| SEO metadata | `seo` | `meta_description` | seo.meta_description |
| Loyalty program | `loyalty` | `points_balance` | loyalty.points_balance |
| B2B company | `b2b` | `account_manager` | b2b.account_manager |
| Inventory tracking | `inventory` | `reorder_point` | inventory.reorder_point |
| Theme customization | `theme` | `custom_layout` | theme.custom_layout |
### Best Practices
1. Group related metafields under same namespace
2. Use descriptive, business-friendly key names
3. Avoid generic names (`data`, `meta`, `custom`)
4. Document namespace/key mapping in app
5. Consider storefront visibility needs (use `visible_to_storefront` flag)
## Metafield Definition Creation
Definitions enable admin UI forms and validation.
### Creating Definition via GraphQL
```graphql
mutation CreateMetafieldDefinition($definition: MetafieldDefinitionInput!) {
metafieldDefinitionCreate(definition: $definition) {
metafieldDefinition {
id
name
namespace
key
type
validations {
name
value
}
}
userErrors {
field
message
}
}
}
```
**Input Variables:**
```json
{
"definition": {
"name": "Product Color",
"namespace": "app",
"key": "product_color",
"description": "Custom color classification for storefront filters",
"type": "single_line_text_field",
"ownerType": "PRODUCT",
"visibleToStorefront": true,
"validations": [
{
"name": "max_length",
"value": "50"
}
]
}
}
```
### Definition with Rich Validation
```graphql
mutation {
metafieldDefinitionCreate(definition: {
name: "Reorder Point"
namespace: "inventory"
key: "reorder_point"
type: "number_integer"
ownerType: "PRODUCT_VARIANT"
description: "Minimum inventory level before reorder needed"
validations: [
{ name: "min", value: "1" }
{ name: "max", value: "10000" }
]
}) {
metafieldDefinition { id }
userErrors { field message }
}
}
```
### Definition for Metaobject Reference
```graphql
mutation {
metafieldDefinitionCreate(definition: {
name: "Related Reviews"
namespace: "app"
key: "related_reviews"
type: "list.metaobject_reference"
ownerType: "PRODUCT"
description: "Customer reviews for this product"
validations: [
{
name: "metaobject_definition_id"
value: "gid://shopify/MetaobjectDefinition/1234567"
}
]
}) {
metafieldDefinition { id }
userErrors { field message }
}
}
```
## Metaobject Type Definition
Create custom data structures for shop.
### Creating Metaobject Definition
```graphql
mutation CreateMetaobjectDefinition($definition: MetaobjectDefinitionInput!) {
metaobjectDefinitionCreate(definition: $definition) {
metaobjectDefinition {
id
type
displayNameKey
fields {
key
type
required
}
}
userErrors {
field
message
}
}
}
```
**Input Variables:**
```json
{
"definition": {
"type": "faq_item",
"displayNameKey": "question",
"description": "Frequently asked questions",
"fields": [
{
"key": "question",
"type": "single_line_text_field",
"required": true,
"description": "FAQ question text"
},
{
"key": "answer",
"type": "rich_text_field",
"required": true,
"description": "FAQ answer (HTML + Markdown)"
},
{
"key": "category",
"type": "single_line_text_field",
"required": false,
"description": "Topic category"
},
{
"key": "display_order",
"type": "number_integer",
"required": false,
"description": "Sort order in list"
}
]
}
}
```
### Creating Metaobject Instances
Once definition created, create records:
```graphql
mutation CreateMetaobject($input: MetaobjectCreateInput!) {
metaobjectCreate(input: $input) {
metaobject {
id
type
fields {
key
value
}
}
userErrors {
field
message
}
}
}
```
**Variables:**
```json
{
"input": {
"type": "faq_item",
"fields": [
{
"key": "question",
"value": "How do I return an item?"
},
{
"key": "answer",
"value": "<p>Returns accepted within 30 days. Visit our <a href=\"/policies/returns\">returns page</a>.</p>"
},
{
"key": "category",
"value": "Returns & Exchanges"
},
{
"key": "display_order",
"value": "1"
}
]
}
}
```
## Querying Metafields
### Product Metafields
```graphql
query GetProductWithMetafields($id: ID!) {
product(id: $id) {
id
title
metafields(first: 10) {
edges {
node {
id
namespace
key
type
value
}
}
}
}
}
```
**Variables:**
```json
{
"id": "gid://shopify/Product/123456789"
}
```
### Filtered Metafields
```graphql
query GetSpecificMetafield($id: ID!) {
product(id: $id) {
id
customColor: metafield(namespace: "app", key: "custom_color") {
value
type
}
sizeChart: metafield(namespace: "app", key: "size_chart_url") {
value
}
}
}
```
### Metaobject Query
```graphql
query GetMetaobject($id: ID!) {
metaobject(id: $id) {
id
type
displayName
fields {
key
value
type
}
}
}
```
### List Metaobjects
```graphql
query ListFAQs {
metaobjects(type: "faq_item", first: 10) {
edges {
node {
id
displayName
question: field(key: "question") { value }
answer: field(key: "answer") { value }
category: field(key: "category") { value }
}
}
}
}
```
### Metaobject References in Products
```graphql
query GetProductWithReferences($id: ID!) {
product(id: $id) {
id
title
relatedGuides: metafield(
namespace: "app",
key: "related_guides"
) {
value
type
reference {
... on Metaobject {
id
displayName
type
}
}
}
}
}
```
## Storefront API Access
### Public Metafield Access
Metafields with `visible_to_storefront: true` accessible via Storefront API:
```graphql
query GetProductStorefront($handle: String!) {
productByHandle(handle: $handle) {
id
title
metafields(first: 10) {
edges {
node {
namespace
key
value
type
}
}
}
}
}
```
### Storefront Query Example
```typescript
// Client-side storefront API query
const query = `
query GetProductMetafields($handle: String!) {
productByHandle(handle: $handle) {
id
title
productColor: metafield(namespace: "app", key: "product_color") {
value
}
manufacturerId: metafield(namespace: "app", key: "manufacturer_id") {
value
}
}
}
`;
const response = await fetch('https://myshop.myshopify.com/api/2026-01/graphql.json', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Shopify-Storefront-Access-Token': PUBLIC_ACCESS_TOKEN
},
body: JSON.stringify({
query,
variables: { handle: 'blue-t-shirt' }
})
});
const { data } = await response.json();
console.log(data.productByHandle.productColor.value);
```
## Liquid Theme Access
### Product Metafield in Liquid
```liquid
{%- assign custom_color = product.metafields.app.custom_color.value -%}
<p>Color: {{ custom_color }}</p>
{%- assign warranty = product.metafields.app.warranty_months.value -%}
<p>Warranty: {{ warranty }} months</p>
{%- assign size_chart = product.metafields.app.size_chart_url.value -%}
<a href="{{ size_chart }}">View Size Chart</a>
```
### Metaobject Reference in Liquid
```liquid
{%- assign faq_list = product.metafields.app.related_faqs.value -%}
<div class="faqs">
{%- for faq in faq_list -%}
<div class="faq-item">
<h3>{{ faq.question.value }}</h3>
<p>{{ faq.answer.value }}</p>
</div>
{%- endfor -%}
</div>
```
### Conditional Display
```liquid
{%- if product.metafields.app.is_discontinued.value -%}
<div class="alert">This product is discontinued</div>
{%- endif -%}
```
### JSON Metafield in Liquid
```liquid
{%- assign compatibility = product.metafields.app.compatibility_matrix.value -%}
<ul>
{%- for platform in compatibility.platforms -%}
<li>{{ platform }}</li>
{%- endfor -%}
</ul>
```
## Visible vs Hidden Metafields
### visible_to_storefront: true
**Accessible:**
- Storefront API (public queries)
- Liquid templates
- Customer-facing applications
- Search engines (SEO indexing)
**Use for:**
- Product colors, sizes
- SEO metadata
- Customer-facing attributes
- Storefront display data
### visible_to_storefront: false
**Accessible:**
- Admin GraphQL API only
- Admin dashboard
- Backend systems
**Use for:**
- Internal tracking IDs
- Supplier information
- Cost/margin data
- Backend workflow flags
## Setting Metafields via GraphQL
### metafieldsSet Mutation
```graphql
mutation SetMetafields($input: MetafieldsSetInput!) {
metafieldsSet(input: $input) {
metafields {
id
namespace
key
value
type
}
userErrors {
field
message
}
}
}
```
**Input Variables:**
```json
{
"input": {
"ownerId": "gid://shopify/Product/123456789",
"metafields": [
{
"namespace": "app",
"key": "custom_color",
"type": "single_line_text_field",
"value": "navy-blue"
},
{
"namespace": "app",
"key": "warranty_months",
"type": "number_integer",
"value": "24"
},
{
"namespace": "seo",
"key": "meta_description",
"type": "single_line_text_field",
"value": "Premium navy blue t-shirt with guaranteed quality"
}
]
}
}
```
### Bulk Setting via GraphQL
```graphql
mutation BulkSetMetafields($input: [MetafieldsSetInput!]!) {
metafieldsSet(input: $input) {
metafields { id }
userErrors { field message }
}
}
```
## Common Gotchas
| Issue | Cause | Solution |
|-------|-------|----------|
| Metafield not visible in Liquid | `visible_to_storefront: false` | Set to true in definition |
| JSON parse error | Invalid JSON string | Validate JSON before setting |
| Reference broken | Referenced object deleted | Add referential integrity checks |
| Type mismatch on update | Changing type after creation | Create new key, deprecate old |
| Admin UI doesn't show | Definition not created | Create definition via mutation |
| Max length exceeded | Value too long | Check validation rules |
| List reference empty | Metaobject definition ID invalid | Verify definition exists |
| Storefront query fails | Missing scopes | Ensure read_products scope |
| Slow metafield queries | Querying too many fields | Limit first parameter, use pagination |
| Cost explosion | Querying all metafields | Limit first parameter, use pagination |
## Performance Considerations
### Querying Efficiently
**Bad - Expensive Cost:**
```graphql
query {
products(first: 100) {
edges {
node {
metafields(first: 250) { # 250 metafields per product!
edges { node { value } }
}
}
}
}
}
```
**Good - Optimized:**
```graphql
query {
products(first: 100) {
edges {
node {
id
customColor: metafield(namespace: "app", key: "custom_color") {
value
}
warranty: metafield(namespace: "app", key: "warranty_months") {
value
}
}
}
}
}
```
### Rate Limit Impact
Metafield operations incur API cost:
- Querying metafield: 1 point
- Setting metafield: 10 points per metafield
- Bulk operations: batch multiple sets
## Best Practices
1. **Define metafields upfront** - Creates admin UI automatically
2. **Use consistent namespacing** - Group related fields logically
3. **Plan for storefront visibility** - Design with customer access in mind
4. **Validate data types** - Use appropriate types to prevent errors
5. **Document your metafields** - Maintain namespace/key reference
6. **Consider performance** - Query only needed metafields
7. **Use metaobjects for reusable data** - More organized than scattered metafields
8. **Prefer references over IDs** - Use product_reference type, not storing IDs
9. **Implement fallbacks** - Handle missing metafields in themes
10. **Version your definitions** - Plan for future schema changes
SHA-256: 054c403a36bfff3884d5ebbf250e70b416a87509d7c154b336eeba1c7bf9eb2c