Sanity Connect is the official Shopify-to-Sanity sync app. The docs cover the happy path. Production is different.
After running Connect across multiple Shopify Plus headless builds, the failure modes are predictable: webhook drops that nobody noticed, variant data that's shallower than the docs imply, metafield data that doesn't sync at all, and the slow accumulation of drift that only shows up when a customer reports a product page rendering wrong content. This is what the docs don't tell you, and the reconciliation pattern we run nightly to keep things consistent.
What Sanity Connect actually does
Sanity Connect is a free Shopify app published by Sanity. Once installed, it does five things:
- Creates a Sanity document for every Shopify product, with a stable shopifyId and shopifyHandle
- Creates a separate Sanity document for every product variant, linked to the parent
- Subscribes to Shopify webhooks so document creates, updates, and deletes propagate within seconds
- Handles the initial bulk import when you first install it
- Soft-deletes Sanity documents when products are removed from Shopify
That's the surface area. It's deliberately narrow. Sanity Connect is not a content sync, a metafield sync, or a bidirectional sync. It's a one-way handle and ID sync that creates the document hooks you need to build enrichment around.
If you're new to the broader stack, our post on why Sanity is the best headless CMS for Shopify covers why this is the right shape for the integration in the first place.
The data model Connect creates
Out of the box, Connect creates two document types in your Sanity schema: shopifyProduct and shopifyProductVariant. They look roughly like this:
// shopifyProduct (auto-created)
{
_type: 'shopifyProduct',
shopifyId: 'gid://shopify/Product/12345',
shopifyHandle: 'merino-crew-sweater',
title: 'Merino Crew Sweater',
productType: 'Sweater',
vendor: 'Brand',
status: 'active',
variants: [{ _ref: 'variant_12345_1', _type: 'reference' }],
updatedAt: '2026-05-01T08:00:00Z'
}
// shopifyProductVariant (auto-created)
{
_type: 'shopifyProductVariant',
shopifyVariantId: 'gid://shopify/ProductVariant/12345-1',
sku: 'MCS-CHA-M',
title: 'Charcoal / M',
price: 189.00,
inventoryId: 'gid://shopify/InventoryItem/...',
parent: { _ref: 'product_12345', _type: 'reference' }
}
The right pattern is to extend, not replace. You leave Connect's auto-managed fields alone and add your own enrichment fields alongside. We typically structure this as a separate productEnrichment document that references the shopifyProduct, so the two concerns stay cleanly separated.
Gotcha 1: One-way sync
The single most common assumption to break early. Sanity Connect syncs Shopify to Sanity only. Editing a product title in Sanity Studio does not push that change back to Shopify. The next webhook from Shopify will overwrite it.
This is a feature, not a bug. Bidirectional sync creates source-of-truth conflicts that are hard to resolve cleanly. The right pattern is to keep Shopify as the canonical source for commerce data and use Sanity for enrichment fields that don't exist in Shopify at all (storyBlocks, lifestyleGallery, careInstructions).
Make this rule explicit with the content team before launch. We've seen new merchandisers spend an hour rewriting product descriptions in Sanity, only to watch them disappear when Shopify pushes the next sync.
Gotcha 2: Variant data is shallow
The variant document Connect creates carries shopifyVariantId, SKU, title, price, and inventoryId. It does not carry the full Shopify variant object. No image references, no metafield data, no inventory levels.
For most use cases, this is fine. Inventory and pricing are queried from Shopify's Storefront API at render time, not from Sanity. But if you're building variant-level merchandising (per-variant hero imagery, per-variant copy, swatch metadata that drives a colour picker UI), the shallow variant document isn't enough.
The fix is a custom variant document with the additional fields you need, populated either manually by merchandisers in Sanity Studio, or via a custom Admin API sync that runs alongside Connect.
Gotcha 3: Metafields don't sync
This is the single biggest reason teams hit the wall with Connect. If your Shopify products use metafields to store structured data (and most mid-market brands do), that data does not appear in Sanity by default.
You have three options:
- Don't sync metafields. Migrate the data to Sanity. The cleanest answer if metafields are duplicating content that belongs in the CMS anyway. Specs, ingredients, and product narrative all belong in Sanity. Migrate them, then retire the metafields.
- Build a custom Admin API sync. A second sync layer that pulls metafield values into Sanity fields. Runs on the same webhooks Connect uses. Maintainable but adds engineering surface area.
- Query metafields at render time. Storefront API supports metafield queries. Skip the Sanity sync entirely and pull metafields directly when rendering the product page. Simplest answer if the metafields are stable and read-only.
Which one you pick is a function of how often the metafields change, who edits them, and whether the data is reused across surfaces. The deeper comparison between metafields and Sanity as content stores lives in Sanity vs Shopify metafields.
Gotcha 4: Webhooks fail silently
Sanity Connect relies on Shopify webhooks to propagate updates. Webhooks are reliable most of the time. They fail occasionally. When they fail, you don't get notified.
Failure modes we've seen in production:
- Shopify retries a webhook 19 times over 48 hours, then gives up. Connect missed all 19.
- Sanity API rate-limits during a bulk operation. Webhooks succeed at the Shopify end but the writes fail downstream.
- A Sanity dataset migration runs while a webhook fires. The webhook completes, but writes to the old schema.
- Shopify Flow modifies a product programmatically with skip_webhook flags set. Sanity never sees the change.
Each one creates drift between Shopify (source of truth) and Sanity (rendered content). Without active reconciliation, drift compounds. We've seen drift of 5-15% of products after six months on builds that didn't have reconciliation in place.
The reconciliation pattern
This is the single most important thing to add to a Sanity Connect implementation that the docs don't mention. A nightly job that:
- Pulls the full active product set from Shopify Admin API
- Pulls the matching shopifyProduct documents from Sanity
- Diffs the two sets on shopifyId, updatedAt, and a checksum of synced fields
- Re-syncs any deltas using the Sanity client
- Logs a summary of products synced, drift detected, errors
- Alerts if drift exceeds a threshold (we use 1% of catalogue)
Pseudocode:
// reconcile.ts (runs nightly)
const shopifyProducts = await shopifyAdmin.products.list({ status: 'active' })
const sanityProducts = await sanityClient.fetch(
`*[_type == "shopifyProduct"]{ shopifyId, title, updatedAt }`
)
const sanityById = new Map(sanityProducts.map(p => [p.shopifyId, p]))
const drift = []
for (const shopifyProduct of shopifyProducts) {
const sanity = sanityById.get(shopifyProduct.id)
if (!sanity || hasChanged(shopifyProduct, sanity)) {
drift.push(shopifyProduct)
}
}
for (const product of drift) {
await sanityClient.createOrReplace(buildSanityDoc(product))
}
await alertIfThresholdExceeded(drift.length, shopifyProducts.length)
It runs in 90 seconds for a 5,000 product catalogue. It's the single highest-leverage piece of operational tooling in the entire stack. The broader integration health pattern is covered in our post on getting your back-office connected.
Gotcha 5: The first publish is a re-sync
When you first install Sanity Connect, it imports your full catalogue. For brands with 5,000+ SKUs, this is a multi-hour operation. The first import also triggers a webhook for every product, which can cause downstream issues if the build is consuming those webhooks for cache invalidation or downstream sync.
Two operational notes:
- Run the initial Connect import on a quiet day, not the day before launch.
- If you have downstream consumers of Shopify webhooks (Klaviyo, ERP, search index), expect a flood when Connect first runs and plan for it.
When to replace Connect with a custom sync
Three triggers usually drive the conversation:
- You need rich variant-level enrichment. The shallow variant document isn't enough, and the workaround documents are getting unwieldy.
- You're metafield-heavy. Most of your structured product data lives in metafields, and Option 2 (custom Admin API sync alongside Connect) has more moving parts than building one sync that does both.
- You're running a multi-store Shopify Plus setup. Connect's single-store model doesn't handle multi-store cleanly. Brands with separate Shopify stores per region or per brand often graduate.
Below those triggers, Connect plus a reconciliation job is the right answer. It's free, supported, and battle-tested. Don't build a custom sync because it feels more sophisticated. Build one because Connect specifically can't do what you need.
The launch checklist
Five things we run before going live with Connect on any new build:
- Confirm content team understands the one-way rule. No edits to Connect-managed fields in Sanity Studio.
- Run a 14-day pre-launch sync. Watch for drift, fix any schema gaps before they're blocking.
- Reconciliation job deployed and running nightly. Alert thresholds tuned to your catalogue size.
- Metafield strategy locked. Pick one of the three options above. Document it.
- Monitor Shopify webhook delivery in Shopify Admin for the first month. If retries spike, investigate before drift compounds.
Get those five things right and Connect runs quietly in the background for years. Skip them and you find out about the gaps when a customer support ticket forces the issue. Talk to our team if you want help running this in production.
Frequently asked questions
Is Sanity Connect free?
Yes. Sanity Connect is a free Shopify app published by Sanity. The cost is your Sanity dataset usage and the engineering time to build around it. No additional licensing.
Can I edit a product in Sanity and sync the change back to Shopify?
No. Sanity Connect is one-way: Shopify to Sanity only. Edits made in Sanity Studio to fields like product title or description never flow back to Shopify. If you need bidirectional sync (rare in practice, because it creates source-of-truth conflicts), you build a custom sync layer using the Shopify Admin API.
How does Sanity Connect handle product variants?
Connect creates a shopifyProductVariant document for each variant, linked back to the parent product. The variant document carries variantId, SKU, title, price, and inventory ID, but not the full Shopify variant object. For variant-level enrichment (per-variant lifestyle imagery, copy, or merchandising), you extend the variant schema in Sanity and populate those fields manually or via a custom Admin API sync.
Does Sanity Connect sync Shopify metafields?
No. Metafield data does not sync into Sanity by default. If you rely on Shopify metafields for structured product data (specs, materials, dimensions), you have two options: build a custom Admin API sync that pulls metafields into a Sanity field, or migrate that data into Sanity entirely and stop using Shopify metafields for the same purpose.
What happens when I delete a product in Shopify?
The corresponding Sanity document is soft-deleted: a deleted flag is set on the document, but the document itself remains in Sanity. This is intentional, so you can preserve enriched content in case the product is restored. To hard-delete, you remove the document manually or via a script.
How often does Sanity Connect sync changes?
Connect uses Shopify webhooks, so changes propagate within seconds for most updates. Bulk operations (e.g. updating 5,000 products) batch through, and full re-syncs take longer. The latency model is event-driven, not scheduled.
Why do my Sanity documents drift out of sync with Shopify over time?
Webhooks fail silently. Network blips, Shopify webhook delivery retries, or rate limits can drop events. Without a reconciliation job, drift accumulates. The fix is a nightly reconciliation script that pulls the full Shopify product set and diffs against Sanity, re-syncing any deltas. We've never run a Sanity Connect implementation in production without one.
When should I replace Sanity Connect with a custom sync?
Three triggers usually drive the move: variant-level enrichment requirements that can't be served by the shallow variant document, metafield-heavy product data that needs to live in Sanity, or multi-store Shopify Plus setups where Connect's single-store model doesn't fit. Below those triggers, Connect plus a reconciliation job is the right answer.
A Shopify Plus Agency for Strategic Design & Advanced Engineering
Building something ambitious?
- Sanity Connect is one-way (Shopify to Sanity). Edits made in Sanity Studio to Connect-managed fields never sync back to Shopify and will be overwritten on next webhook.
- Variant documents are shallow: variantId, SKU, title, price, inventoryId. No image refs, no metafield data. Variant-level enrichment requires a custom variant schema.
- Shopify metafields don't sync into Sanity. Three options: migrate metafields to Sanity entirely, build a custom Admin API sync alongside Connect, or query metafields at render time via Storefront API.
- Webhooks fail silently. Drift between Shopify and Sanity compounds without active monitoring. Run a nightly reconciliation job that diffs the full product set and re-syncs deltas.
- The launch checklist: one-way rule documented, 14-day pre-launch sync run, reconciliation job live, metafield strategy locked, webhook delivery monitored for the first month.
- Replace Connect with a custom sync only when you genuinely need bidirectional updates, deep variant enrichment, multi-store support, or full metafield coverage. Otherwise Connect plus reconciliation is the right answer.





