← Shopify App BuilderCONTENT HISTORY

Update to Shopify App Builder

Snapshot Sep 30, 2026 · 23:13 UTC · version 1.4.1

Collection source: not recorded for this historical snapshot.

WHAT CHANGED · RULE-BASED ANALYSIS

First saved snapshot

No earlier snapshot is available to establish a change.

Compare saved observations

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

SHA-256 of public snapshot: 2bb92ac029b1e5e59b4e9b454533e626f2a269699fa3d66ba9351e4128617c55