Skip to main content
Shop Canvas Docs

Field map

Every field the catalog sync writes into your Framer CMS collection, what it holds, and which Shopify value it comes from — including the two encoded strings that drive variants and variant imagery.

Where this comes from

This describes the sync as it is implemented in lib/sync-core-body.js, which is what both the plugin and the CLI run. If a field here does not match what you see in Framer, the sync is right and this page is stale — say so.

How to read this

The sync writes one CMS item per Shopify product. The item's slug is the product handle, so a product at /products/forest-tote in Shopify becomes the CMS item forest-tote.

Fields are created in the order below. That order matters only once: Framer shows them in it. Nothing reads a field by position — components bind by name.

Identity

CMS fieldTypeShopify sourceNotes
Titlestringtitle
Product IDstringidThe numeric part only. Shopify returns gid://shopify/Product/7058148753547; the sync keeps 7058148753547.
Date CreateddatecreatedAt

Price and availability

CMS fieldTypeShopify sourceNotes
PricestringpriceRange.minVariantPrice.amountThe lowest variant price, as a bare number to two decimals — 49.00. No currency symbol, so existing numeric bindings keep working. Empty if the amount is missing or unparseable.
Formatted Pricestringsame, plus currencyCodeThe display form, currency as a code rather than a symbol — AED 49.00. A separate field so adding it could not disturb bindings already pointing at Price. Empty if the amount is negative or the currency is not three letters.
Compare PricestringcompareAtPriceRange.minVariantPriceSame bare-number format as Price. Empty when nothing is on sale.
AvailabilitystringavailableForSaleWritten as the schema.org values InStock or OutOfStock, not true/false, so Product markup can state availability honestly rather than assuming stock.

Imagery

CMS fieldTypeShopify sourceNotes
Image 1 … Image Nimageimages.nodes[n]One field per slot. Alt text comes from the image's own altText, falling back to the product title. Slots beyond the product's image count are left empty.
Video 1 … Video Nfilemedia.nodes where type is VIDEOProgressive MP4 only. Shopify also returns an HLS .m3u8 source, which a Framer file field cannot accept; the sync drops it and takes the largest remaining MP4. Accepts mp4, mov, webm.

The number of slots is per project, set in projects/<name>.json. It is a fixed count because Framer CMS fields are a schema, not a list — a product with more images than slots loses the extras.

ProjectimageSlotsvideoSlots
Odeem131
Astra102
Avnzor81
Template, Scratch, Example81
unset131

Text and taxonomy

CMS fieldTypeShopify sourceNotes
DescriptionformattedTextdescriptionHtmlThe rich HTML, not the plain-text description.
SEO Titlestringseo.title → titleFalls back to the product title when the SEO field is blank.
SEO Descriptionstringseo.description → summarised descriptionFalls back to the product's own opening sentence. A product with no meta description otherwise inherits the site's, so every product reads identically in search results.
Vendorstringvendor
Product TypestringproductType
Product TagsstringtagsJoined with , into one string.
Collectionsstringcollections.nodes[].titleCollection titles, joined with , . Up to 50. Handles are not written.

Ratings

CMS fieldTypeShopify sourceNotes
Ratingstringmetafield with key ratingLoox writes this as a JSON blob; the sync unwraps it to a plain number so aggregateRating can use it directly.
Rating Countstringmetafield with key rating_count

These two are matched by metafield key, not by full name — any namespace works, but the key must be exactly rating / rating_count, and the metafield must be listed in the project's metafields. Astra lists reviews.rating and reviews.rating_count, so both populate there. A project that lists neither gets two empty fields, not an error.

Metafields

Anything listed in a project's metafields array gets its own CMS field, named with the full dotted name — custom.material_care, not material_care. That naming matches what the plugin originally emitted, so existing bindings keep working.

{
  "metafields": ["custom.material_care", "custom.bag_dimension", "custom.size"],
  "richTextMetafields": ["custom.material_care"]
}

Each entry is one string split on the first dot: namespace before, key after. Fields are string unless also listed in richTextMetafields, which makes them formattedText.

The two encoded strings

These are the fields people find surprising. Both pack structure into a single string field, because Framer CMS has no repeatable or nested type.

Variants

[Color: White, Yellow • 7058148753547] [Size: XS, S • 7058148753547]
  •• [White / XS • 41246066704523] [White / S • 41246066704524]

One bracket per option listing every value, then ••, then one bracket per concrete variant with its own id. Ids are numeric, same treatment as Product ID.

A product with no real options gets an empty string. Shopify gives those a synthetic Default Title variant, and the sync skips it deliberately — matching what the plugin wrote, so single-variant products stay unchanged.

Image Variant IDs

[Image 1 • 41246066704523][41246066704524][Image 3 • 41246066704530]

Grouped by image, in image order, naming the slot once per group. Every variant sharing one image — all five sizes of a colour — rides in that image's group, which is what lets a gallery map a variant to its photo and back.

This field is preserved when empty: if the sync computes nothing for it but the CMS already holds a value, the existing value stays, so a store that has mapped its imagery cannot lose that mapping to a sync that happened to return nothing.

It also stays with its photos. The map names photo slots, so when Keep existing Framer photos is on, an item that already exists keeps its map as well as its photos. A map rebuilt from Shopify would follow Shopify's photo order, and Shopify gives each variant a single photo, so it could point a colour at the wrong photo or drop its second one. New items still get the map Shopify implies, alongside Shopify's photos.

What the sync will not overwrite

  • Image Variant IDs, as above: when the new value would be empty, and on existing items whenever their photos are kept.
  • Photo fields on existing items, if you choose Keep existing Framer photos during a review. That skips photo fields on items that already exist, including empty ones — it cannot tell which photos were curated by hand. Note that Product details renders Shopify images directly, so this setting does not affect it.
  • Fields you have overridden in a review. Per-field Keep / Use Shopify choices for titles and descriptions persist in project metadata, and a change to the catalog, schema, CMS or configuration invalidates the review and requires a fresh preview.

Things worth knowing before you bind

  • Prices are strings, not numbers. Price is a formatted decimal. Sorting on it in Framer sorts lexically.
  • Collections is a flat string of titles. Filtering by collection means string matching, not a relation.
  • Slot counts are a ceiling. A 20-image product in an 8-slot project silently loses 12 images. Raise imageSlots and re-sync.
  • Every product write is read back. A partial or unknown result requires a fresh preview rather than being assumed.

On this page