Skip to Content

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

VariantDestinationWhen to use
A. Vendo Ingestion (recommended)Your Vendo ingest API → any configured destinationYou 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 DirectBrowser → api-js.mixpanel.comMixpanel 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

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 tracking field 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-key

Your Vendo onboarding contact provides both values.

5. Verify

  1. Build and run your Hydrogen storefront.
  2. Browse a couple of pages and view a product.
  3. 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 /collect requests, batches containing page, track, and identify events.
  4. 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-browser

2. 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=false

Set PUBLIC_MIXPANEL_DEBUG=true in development for verbose Mixpanel console logging.

6. Verify

  1. Build and run your Hydrogen storefront.
  2. Browse a couple of pages and view a product.
  3. In your browser’s Network tab, filter by api-js.mixpanel.com. You should see POSTs with Page Viewed, Product Viewed, etc.
  4. 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 eventTracker callWhen it fires
page_viewedpage() / track('Page Viewed')On every route change
product_viewedtrack('Product Viewed', ...)On product detail page load
collection_viewedtrack('Collection Viewed', ...)On collection page load
cart_viewedtrack('Cart Viewed', ...)When the cart drawer is opened
cart_updatedtrack('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:

VariantBridge
Vendo IngestionCalls 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 DirectUses _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_viewed event 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 in connect-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

Last updated on