Skip to Content

Collection mapping

StagingCollection is a flat shape — title, image, optional product list, and a few menu hierarchy hints that get stored as metafields.

A collection can be manual (a fixed product list, via products here or collections on the product mapping), rule-driven (map rules to recreate the source’s automated collections), or metafield-driven smart (smart_collection: true, matching products by the collection names in their custom.collections metafield).

Quick reference — minimum loadable collection

{ "original_id": category_id, "title": name, "handle": $lowercase(url_key) }

Identity & display

titlestring

Collection display name.

"title": name
handlestring

URL slug. Auto-derived from title if omitted.

"handle": $lowercase(url_key)
descriptionHtmlstring

Collection description with HTML.

"descriptionHtml": description
sortOrderstring

How products are ordered within the collection. Common values: MANUAL, BEST_SELLING, ALPHA_ASC, ALPHA_DESC, PRICE_ASC, PRICE_DESC, CREATED_DESC. Defaults to Shopify’s default if omitted.

"sortOrder": "MANUAL"
templateSuffixstring

Custom theme template suffix. Use only if the merchant has a non-default collection template.

Image

Collection cover image. Two flat fields — different shape from product images.

image_srcstring

URL of the collection’s cover image. Setting this is what triggers the image being created.

"image_src": image_url
image_altstring

Alt text for the cover image.

"image_alt": image_alt ? image_alt : name

Products

The product list lives on the collection or on each product — pick one.

productsstring[]

Array of source-platform product IDs to include in the collection. Graftport resolves these to Shopify GIDs at load time using the product migration’s results.

"products": product_ids

You don’t have to populate products here — alternatively, set collections: [<this collection's original_id>] on the product mapping and the membership goes the other direction. Pick one approach per migration; mixing is confusing.

rulesobject[]

Turns the collection into an automated collection: Shopify decides membership from these conditions and keeps deciding, instead of holding the fixed list you migrated.

Each rule is { column, relation, condition } — for example {"column": "TAG", "relation": "EQUALS", "condition": "wool"}.

"rules": [{ "column": "VENDOR", "relation": "EQUALS", "condition": brand_name }]

Mapping rules drops products: a collection is either rule-driven or a fixed list, never both.

rules_match_anyboolean

true when a product only has to match one rule, false when it has to match them all. Required whenever rules is mapped — there is no default, so a rule-driven collection that leaves this out fails validation rather than guessing which behavior you meant.

smart_collectionboolean

Creates this collection as a smart collection matching on a product metafield: its single rule is “the product’s custom.collections metafield contains this collection’s reference name”. Graftport sets up the metafield definition on the destination shop automatically. Fill the metafield from the product mapping’s smart_collections field.

"smart_collection": true

Mutually exclusive with rules and with a manual products list — the metafield rule is the membership. Requires title and the source ID, which together form the default reference name.

smart_collection_referencestring

The reference name the smart collection matches on — the join key between this collection and the values in each product’s custom.collections metafield. You normally don’t map this: it defaults to “Collection name #id” (“Dame #2915”, “Tilbehør #2760”), which is unique even when many collections share a name, and still readable where it matters — on the product in Shopify admin. Plain names are not used as the join key on purpose: category trees routinely reuse names (“Dame” under both Trøjer and Jakker), and a name-keyed rule would put a product into every same-named collection.

Map it explicitly only as an exception — most usefully to pin the previous reference after renaming a collection: the reference is locked once the collection has loaded (changing it would silently empty the collection, because loaded products still carry the old value), so a rename keeps the old reference unless you re-load the products too.

"smart_collection_reference": name & " #" & category_id

Two collections resolving to the same reference name fail validation loudly — never silently — since a shared join key would merge their memberships.

SEO

meta_titlestring
meta_descriptionstring

Menu hierarchy (auto-metafields)

Source platforms with category trees often need this preserved. The staging model auto-converts these to custom metafields.

include_in_menuboolean

Whether the source category was visible in the storefront menu. Set it to false to keep a category out of the generated menu. Its subcategories move up to the nearest category that is in the menu, so they aren’t lost. Also becomes a custom.include_in_menu boolean metafield for theme code.

is_menu_rootboolean

Whether this category was a top-level / root menu entry. Becomes a custom.is_menu_root boolean metafield.

parent_idstring

The category this one sits under, using the source store’s own ID for the parent. This is what Storefront navigation builds the menu from, so map it whenever the source has a category tree. It also becomes a custom.parent_id metafield, so theme code can read the hierarchy directly.

Leave it empty (or null) for top-level categories.

Patterns

Magento category → Shopify collection

{ "original_id": entity_id, "title": name, "handle": url_key, "descriptionHtml": description, "image_src": image, "image_alt": name, "include_in_menu": include_in_menu = "1", "is_menu_root": level = 2, "parent_id": parent_id, "meta_title": meta_title, "meta_description": meta_description }

Membership-on-collection (push the product list explicitly)

{ "original_id": entity_id, "title": name, "handle": $lowercase(name), "products": products.product_id }

In the product mapping, list each product’s collection memberships:

{ "original_id": entity_id, "title": name, "collections": $append(category_ids, ["all-products"]), }

Then in the collection mapping, just declare the collection itself — no need to list products:

{ "original_id": entity_id, "title": name, "handle": $lowercase(name) }

Metafield-driven smart collections

Like membership-on-product, but the membership stays visible and editable on each product in Shopify admin: the product carries its collections’ reference names (“Dame #2915”) in a custom.collections metafield, and each collection is a smart collection matching on its own reference.

In the product mapping:

{ "original_id": entity_id, "title": name, "smart_collections": category_ids, }

In the collection mapping:

{ "original_id": entity_id, "title": name, "handle": $lowercase(name), "smart_collection": true }

Hierarchical category names

If the merchant wants the path encoded in the title:

{ "original_id": entity_id, "title": $join($append(parent_path, [name]), " / "), "handle": $join(parent_path & [name], "-") ~> $lowercase }

Gotchas

A collection can’t switch kind after it loads. Shopify doesn’t convert a manual collection into a smart one (or back) on update, so a collection first loaded without rules/smart_collection and later re-mapped with them fails to update. Delete the collection in Shopify admin (or purge and re-run) to change kind.

  • Membership conflict. If a product references a collection in its collections array AND the collection’s products array also includes that product, the second load wins. Avoid the conflict by picking one direction.
  • image_src without image_alt still loads — the alt is just empty. Set it from the source name as a sensible default.
  • sortOrder values are case-sensitive. Shopify accepts the exact uppercase enum values listed above; "manual" (lowercase) fails.
  • Handle collisions. Two collections with the same auto-derived handle silently overwrite. If two source categories have similar names (News and news), set handle explicitly with a disambiguating suffix.
  • The menu is built separately. Mapping parent_id and include_in_menu records the tree on each category; it doesn’t create the storefront menu by itself. Once the load finishes, use Storefront navigation to turn that tree into a real Shopify menu.
Last updated on