← All guides9 min read

Mapping Magento custom attributes to Shopify metafields in Graftport

How Graftport's mapping editor handles Magento EAV custom attributes when they don't fit Shopify's variant model. The metafield contract, naming, dry-run verification, and what makes it onto your storefront.

Magento's flexibility — and most of the work in any Magento → Shopify replatform — comes from EAV (entity-attribute-value) storage. Every custom product attribute, every customer attribute, every category attribute is a row in EAV. Shopify does not have EAV. It has variants (a fixed-shape, finite-value option model) and metafields (typed key-value pairs attached to any record).

This guide is the practical contract for how Graftport's mapping editor turns the first into the second, and what to verify on staging.

The decision tree

For each Magento product attribute, the mapping editor evaluates, in order:

  1. Is it a system Shopify field? (title, body_html, vendor, product_type, tags, handle, seo_title, seo_description.) If yes, map to that Shopify field directly.
  2. Is it an option that becomes a variant axis? (Size, Color, Material — finite, enumerable values, used in product configuration.) If yes, lift into Shopify variants.
  3. Otherwise, store as a metafield under a stable namespace.

The default mapping seeded by the wizard already covers (1) and (2) for the standard Magento attribute set. Step (3) is the part you review per-store, because every Magento install accumulates custom attributes nobody fully documented.

The metafield contract

Graftport's default for a Magento attribute that does not fit a system field or a variant becomes a Shopify metafield with this shape:

PropertyDefault value
Namespacemagento (configurable per-mapping)
KeyThe Magento attribute code, lowercased
TypeInferred from the Magento attribute's input type
DescriptionThe Magento attribute's frontend label

So a Magento attribute coded kit_month with input type text lands as magento.kit_month of metafield type single_line_text_field. A material_origin attribute with input type select lands as magento.material_origin of type single_line_text_field too — Graftport stores the option label as the value, not the option ID, because option IDs are Magento-internal noise that Shopify cannot resolve.

The point of holding onto the original attribute code: if your Magento theme references getAttribute('kit_month') somewhere, your new Shopify theme can reference metafields.magento.kit_month and get the equivalent value out. Direct one-to-one translation, storefront-side.

Magento input type → Shopify metafield type

Magento input typeShopify metafield type
Text Fieldsingle_line_text_field
Text Areamulti_line_text_field
Yes/Noboolean
Datedate
Datetimedate_time
Pricemoney (currency from store)
Decimalnumber_decimal
Integernumber_integer
Dropdown / Selectsingle_line_text_field (label)
Multi-Selectlist.single_line_text_field
Media Imagefile_reference (asset uploaded)
Visual Swatchsingle_line_text_field (label)
Text Swatchsingle_line_text_field (label)

When Graftport meets an attribute it cannot type cleanly (typically a custom plugin's input type), it falls back to single_line_text_field and stores the raw string value. The mapping editor flags this with a warning so you can override.

Editing the mapping for one attribute

From your migration's home screen, click Resources → Products → Edit mapping. The mapping editor opens with three columns:

  • Source schema (left). Every attribute Graftport found on a sample of your Magento products. Hover any row to see the inferred type and a sample value.
  • Mapping (middle). The current rule for each attribute: what Shopify field it lands on, with what transform.
  • Destination schema (right). Shopify's product schema with metafield slots highlighted by namespace.

To override an attribute's mapping:

  1. Click the attribute on the left.
  2. The mapping rule drawer slides in with the current rule.
  3. Change the destination type: pick a different metafield namespace, switch from metafield to a system field, or to "drop".
  4. Optionally apply a transform: prefix, lowercase, parse-to- number, etc. Graftport ships a small library of common transforms; you can also write a SQL expression for anything custom.
  5. Click Save. The change is versioned in app.mapping_template_version.

Dry-running the mapping change

Before re-loading, dry-run the change:

  1. Go back to the migration's home screen.
  2. Start a run → Mode: Transform only → Dry-run on.
  3. The transform run reuses the last extract's data and applies the new mapping. No source-side reads, no destination writes.
  4. Open Data tab on the run, pick a representative product, and confirm the metafield came out as expected.

When the transformed shape looks right, run a full load (dry-run off). Already-loaded products are re-synced at the (cheaper) sync rate; the metafield change lands without touching anything else.

Mapping decisions that actually matter

Most attributes fall on the seeded defaults and never need a hand. The ones that do, in order of how often we hit them:

  • Magento attributes used in cart price rules. Rules like "10% off if customer_group = wholesale" need the customer-side attribute migrated. Move it to customers.metafields.<your-ns>.<key> and rebuild the equivalent rule as a Shopify automatic discount using the metafield value.
  • Free-form visual swatches that store image filenames. The default lands the filename as a string. If you want the image itself uploaded to Shopify Files and referenced by ID, switch the destination type to file_reference. Graftport handles the upload during transform.
  • Multi-store-specific attributes. A Magento attribute can have per-store-view values. Pick the store view you are migrating (in the wizard notes) and Graftport reads only that view's values. If you want both, fork the migration and run two.
  • Attributes never used. The mapping editor's source-schema panel shows which attributes are actually populated on at least one product. If a custom attribute is on zero products, set its mapping rule to drop. Cleaner storefront, fewer metafields to wade through later.

The no-op trap

A common mistake: Magento has the attribute, the migration moves the metafield, but the new Shopify theme never reads the metafield. Two ways to verify:

  • The metafield is on the product. Open the staging Shopify Admin → Products → click a product → scroll to the metafields section. Confirm magento.<attribute_code> is listed with the expected value.
  • The theme renders it. Most Online Store 2.0 themes need an explicit "use this metafield" config. Open Shopify Admin → Online Store → Customize → pick the relevant section → bind a block to the metafield. If your theme is custom, the dev reference is {{ product.metafields.magento.<key> }}.

If the metafield is on the product but not on the storefront, the migration is correct — the theme is the gap.

What we do not migrate as metafields

A few Magento attribute types never become metafields, by design:

  • url_key. Becomes the Shopify product handle directly. The url_rewrite table becomes 301 redirects on the destination Shopify store under Online Store → Navigation → URL Redirects.
  • status (1/2 enabled/disabled). Disabled products are not migrated. There is no equivalent Shopify metafield; "available on storefront" is a Shopify product status field.
  • tax_class_id. Becomes the Shopify product's tax-collected/exempt flag plus the destination store's tax zones, not a metafield. Tax in Shopify is configured per-region, not per-product-attribute.
  • weight and dimensions. Land in Shopify's native shipping fields (variant weight + product dimension metafields under the reserved shipping namespace).

Re-running after a mapping change

Mappings are versioned. Editing a mapping creates a new mapping_template_version. The migration uses whichever version you have set as current. Re-running the migration after a mapping change re-transforms every record that was previously loaded with the old mapping and re-syncs the destination.

This is how you fix a metafield mistake on day three without re-pushing the catalogue from scratch: edit the mapping, dry-run transform, re-run with load. Records that already match the new shape are skipped.

Related reading

When you are ready to drill into your own attribute set, head to Resources → Products → Edit mapping in your migration. Most overrides land in under five minutes.

Ready to migrate?

Connect a source store, dry-run a migration, see the exact Shopify result before a single record lands. The same platform your team will use on go-live night.

Get started See the calculator
Related guides
Using the Graftport CLI with Claude Code: install to first publish
Set up the Graftport CLI and migration-engineer skill in Claude Code, then drive a real Shopify migration through the validate and publish l
Human approval gates for AI Shopify migrations: the Graftport contract
Why an AI coding agent should never push to Shopify on its own, and how Graftport's two-tier CLI contract enforces a human gate on every cos
Automating JSONata Shopify mapping with an AI coding agent
How the Graftport CLI lets an AI agent investigate source rows, fix JSONata mapping errors against structured validation codes, and publish