Headless Shopify Tracking
For headless Shopify storefronts built on Hydrogen , the standard Vendo Shopify app’s web pixel doesn’t apply — there’s no Online Store theme for Shopify to inject it into. Instead, Vendo hooks into Hydrogen’s own useAnalytics event bus and forwards events from there.
We maintain a reference Hydrogen starter plus drop-in files for existing projects:
Repository: vendo-analytics/vendo-shopify-headless-tracking (private — your Vendo contact will grant access).
How it works
Browser
│
▼
Hydrogen <Analytics.Provider> ── publishes events ──▶ useAnalytics() bus
│
▼
<VendoTracking>
│
┌───────────────┴───────────────┐
▼ ▼
Variant A: vendo.track() Variant B: mixpanel.track()
│ │
▼ ▼
Your Vendo ingest API api-js.mixpanel.com
│
┌─────────────────┼─────────────────┐
▼ ▼ ▼
Mixpanel OneSignal BigQuery (or any
configured destination)Hydrogen emits standard analytics events (page_viewed, product_viewed, cart_updated, etc.) on its internal bus. The Vendo tracker subscribes to these and forwards them to your chosen destination.
Choose your variant
| Variant | Destination | When to use |
|---|---|---|
| A. Vendo Ingestion (recommended) | Your Vendo ingest API → any configured destination | You want one integration that can fan out to Mixpanel, OneSignal, Segment, BigQuery, etc. — and pick or change destinations from Vendo without re-deploying. |
| B. Mixpanel Direct | Browser → api-js.mixpanel.com | Mixpanel is your only destination and you want zero dependency on Vendo’s ingest infrastructure. |
If you’re unsure, start with Vendo Ingestion. It’s destination-agnostic and avoids re-deploying your Hydrogen storefront when destination preferences change.
Prerequisites
- A Hydrogen storefront (this guide targets Hydrogen 2025.x with React Router 7)
- Node.js 18+
- A Vendo workspace
- Variant A only: your Vendo ingest host URL and write key (we provide these during onboarding)
- Variant B only: a Mixpanel project token
Path A — Vendo Ingestion (recommended)
1. Add the tracker file
Copy drop-in/vendo-ingestion/VendoTrackingIngestion.jsx into app/components/VendoTrackingIngestion.jsx in your Hydrogen project.
2. Wire it into app/root.jsx
Apply the patch in drop-in/vendo-ingestion/snippets/root.jsx.diff. It adds three things:
- An import for the tracker component
- A
trackingfield in your root loader’s return value (forwards env vars to the client) - A
<VendoTracking config={data.tracking} />render inside<Analytics.Provider>
3. Allow your ingest host in CSP
Apply the patch in drop-in/vendo-ingestion/snippets/entry.server.jsx.diff. It adds your ingest host to both connect-src (so the SDK can POST events) and script-src (so the browser can fetch <host>/v1/sdk.js).
4. Set env vars
PUBLIC_VENDO_INGEST_HOST=https://track.your-domain.com
PUBLIC_VENDO_WRITE_KEY=your-vendo-write-keyYour Vendo onboarding contact provides both values.
5. Verify
- Build and run your Hydrogen storefront.
- Browse a couple of pages and view a product.
- In your browser’s Network tab, filter by your ingest host. You should see:
- A
GET /v1/sdk.js(loaded once on first page) - One or more
POST /collectrequests, batches containingpage,track, andidentifyevents.
- A
- In your Vendo workspace, open the Live Events view for your source. New events should appear within seconds.
Path B — Mixpanel Direct
1. Install mixpanel-browser
npm install mixpanel-browser2. Add the tracker file
Copy drop-in/mixpanel/VendoTrackingMixpanel.jsx into app/components/VendoTrackingMixpanel.jsx.
3. Wire it into app/root.jsx
Apply the patch in drop-in/mixpanel/snippets/root.jsx.diff. Same shape as Path A, but it imports VendoTrackingMixpanel and forwards mixpanelToken + mixpanelDebug env vars.
4. Allow Mixpanel hosts in CSP
Apply the patch in drop-in/mixpanel/snippets/entry.server.jsx.diff. It adds https://api-js.mixpanel.com and https://api.mixpanel.com to connect-src.
5. Set env vars
PUBLIC_MIXPANEL_TOKEN=your-mixpanel-project-token
PUBLIC_MIXPANEL_DEBUG=falseSet PUBLIC_MIXPANEL_DEBUG=true in development for verbose Mixpanel console logging.
6. Verify
- Build and run your Hydrogen storefront.
- Browse a couple of pages and view a product.
- In your browser’s Network tab, filter by
api-js.mixpanel.com. You should see POSTs withPage Viewed,Product Viewed, etc. - In your Mixpanel project’s Live View, the same events should appear.
Event reference
Both variants forward the same five Hydrogen events. Each is published by <Analytics.Provider> and its built-in helpers (<Analytics.ProductView>, <Analytics.CollectionView>, etc.) that the Hydrogen skeleton routes already use.
| Hydrogen event | Tracker call | When it fires |
|---|---|---|
page_viewed | page() / track('Page Viewed') | On every route change |
product_viewed | track('Product Viewed', ...) | On product detail page load |
collection_viewed | track('Collection Viewed', ...) | On collection page load |
cart_viewed | track('Cart Viewed', ...) | When the cart drawer is opened |
cart_updated | track('Cart Updated', ...) | When items are added, removed, or quantity changes |
Adding custom events
To track additional events (e.g. search_submitted, wishlist_added), add a new subscribe(...) block to your tracker component and a matching publish(...) call in the component that triggers it. See Shopify’s analytics docs for the full pub/sub API.
Identity model
Headless storefronts hand off to Shopify’s hosted checkout, which means the browser identity and the checkout identity must match for cross-boundary attribution to work.
Both variants bridge identity using Shopify’s _shopify_y first-party cookie:
| Variant | Bridge |
|---|---|
| Vendo Ingestion | Calls vendo.identify(null, { shopify_visitor_id }) on first event. The Vendo anonymousId is then associated with the Shopify visitor ID, so order events that arrive server-side (via your Shopify destination → Vendo) stitch to the same profile. |
| Mixpanel Direct | Uses _shopify_y as the Mixpanel distinct_id. Order events sent through your Vendo→Mixpanel pipeline that include the same _shopify_y value land on the same Mixpanel profile. |
If _shopify_y is unset (e.g. user has third-party cookies disabled in some configurations), the tracker falls back to the SDK’s own anonymous ID. You’ll still capture browse activity, but stitching to checkout-side events for that visitor is best-effort.
Limitations
- Checkout pages. Checkout runs on Shopify’s domain, so storefront-side tracking stops at “Checkout started.” Capture order events server-side via your Shopify webhook → Vendo pipeline.
- Server-side rendering. Hydrogen renders pages on Oxygen workers, but the tracker is browser-only — it does nothing during SSR. The
page_viewedevent fires after hydration. - CSP nonce. Hydrogen uses CSP nonces by default. Both tracker scripts are added inside the Hydrogen
<Analytics.Provider>lifecycle, so they inherit the correct nonce automatically. If you customize CSP, make sure the ingest host (Variant A) or Mixpanel hosts (Variant B) remain inconnect-src/script-src.
Troubleshooting
No events in network tab. Check the browser console for [Vendo] warnings. Most likely cause: env vars not set, or your root loader isn’t passing tracking to the <VendoTracking> component.
CSP errors blocking the SDK. Confirm entry.server.jsx includes the right origins for the variant you’re using. For Variant A, both connect-src and script-src need to include PUBLIC_VENDO_INGEST_HOST.
Events fire but don’t appear in Mixpanel/Vendo. Verify the write key (Variant A) or project token (Variant B) is correct and matches the environment. In Variant A, also confirm the ingest API is healthy via GET <host>/health.
Duplicate events. If you see each event fire twice, the tracker is likely being mounted twice — check that you have a single <VendoTracking> render inside <Analytics.Provider>.
Next steps
- Connect downstream destinations in your Vendo workspace (Variant A) — see the Destinations catalog.
- Set up historical backfill for order history.
- Need help? Email support@vendodata.com.