Skip to main content

Cart Sync for Headless Storefronts

Integrate the Tapcart Cart Sync JavaScript SDK on a headless or custom storefront so logged-in carts stay aligned with the Tapcart app.

Headless and custom storefronts do not use a Shopify theme, so Cart Sync uses a lightweight JavaScript SDK instead of the theme app embed. One script tag and one init call wire Tapcart to your existing cart and customer auth.

Sync is logged-in user only and best-effort: it never blocks or breaks your cart UI, and checkout stays 100% native Shopify.

Before you start

  • Your app must be on Tapcart React Native release 21.8.0 or newer.

  • Cart Sync must be enabled for your app. In the Tapcart dashboard, open Settings → Cart Sync, turn Enable Cart Sync on, configure abandoned-cart settings if needed, and save. If you do not see that page, contact Tapcart to enable it.

  • Have your Tapcart App ID ready.

    • Open the Tapcart dashboard

    • Click the shop name (top right) → Settings

    • In App Settings, scroll to the Tapcart CLI / Application ID section

    • Copy the App ID

1. Allowlist your web origins

  1. Go to Settings → Cart Sync → Allowed Web Origins.

  2. Add every storefront origin you will serve from, including staging if needed.

Use a bare origin only: scheme + host + optional port, with no path. Example: https://shop.example.com

Origin allowlist updates typically take effect within about a minute. If the SDK returns a forbidden-origin error, double-check the exact origin string.

2. Load the SDK

<script src="https://api.tapcart.com/cart-sync/sdk/v1/cart-sync.js"></script>

The script defines window.TapcartCartSync. Nothing runs until you call init.

3. Initialize with your cart and auth

const sync = window.TapcartCartSync.init({
  appId: '<your Tapcart App ID>',
  // Logged-in shopper's Shopify customer token, or null for guests.
  // Customer Account API token (shcat_...) or classic customerAccessToken.
  getCustomerToken: async () => myAuth.getShopifyCustomerToken(),
  // Current cart as plain lines.
  readCart: async () => myCart.lines.map(l => ({
    variantId: l.merchandiseId, // Storefront variant id (gid or numeric)
    quantity: l.quantity,
    sellingPlanId: l.sellingPlanId || null,
    properties: l.attributes || {}, // line-item properties/attributes
  })),
  // Apply Tapcart's reconciliation through YOUR cart layer.
  applyCartOps: async ({ upserts, removals }) => {
    for (const u of upserts) await myCart.setLine(u.variantId, u.quantity, u);
    for (const r of removals) await myCart.removeLine(r.variantId, r);
  },
  // Optional:
  // debounceMs: 2500,
  // onSynced: ({ phase, revision }) => {},
  // onError: ({ phase, error }) => {},
});

4. Call hydrate and capture

sync.hydrate(); // on page load AND right after login
myCart.onChange(sync.capture); // after every local cart mutation (debounced)// Also available:
// sync.captureNow()  // e.g. beforeunload
// sync.destroy()
  • hydrate() pulls the server mirror and uses applyCartOps so your cart converges to the shopper's cross-surface state.

  • capture() reports your current cart lines so other surfaces (including the app) can converge to them.

That is the full integration; no webhooks or metafields are required on your side.

How it Works

Identity

It works with Logged-in customers only. If getCustomerToken returns null, sync pauses cleanly. After login, call hydrate() so the account mirror flows in.

Your cart stays yours

Tapcart never creates or adopts Shopify carts and never writes cart metafields. Every read and write goes through the callbacks you provide. Checkout continues to use your live Shopify cart.

Merging

Per-line last-write-wins with deletion tombstones, merged server-side. A line missing from a stale snapshot is never treated as a deletion. Only explicit removals delete, so an empty or stale mirror cannot wipe a real cart.

Corruption safety and failures

Only plain line items sync. If a cart goes bad, discard it, build a fresh one, and call hydrate(). Network or auth failures surface through onError and retry on the next capture or hydrate. The SDK does not throw into your cart code paths.

Data lifecycle

The mirror is keyed by app and customer, cleared on order completion, expires after 10 days idle, and is removed on customer redaction.

Enablement checklist

  1. Add production and staging origins to Allowed Web Origins.

  2. Enable Cart Sync in Settings → Cart Sync.

  3. Add the script and init on your site.

  4. Verify by logging in, mutating the cart on web, and confirming the same lines in the app (and the reverse).

If you also want unified wishlist on the same headless storefront, allowlist the origin under Mobile Wishlist as well and load the Wishlist SDK separately.

Troubleshooting

Forbidden origin errors

Add the exact bare origin to Allowed Web Origins and retry after about a minute.

Feature disabled errors

Turn Enable Cart Sync on in Settings → Cart Sync and save.

Sync appears to do nothing

Confirm the shopper is logged in and that getCustomerToken returns a valid Shopify customer token. Returning null is an intentional no-op for guests.

More Questions

Need help with a headless Cart Sync integration? Reach out through LiveChat on your Tapcart Dashboard or email us at [email protected].

Did this answer your question?