--- title: Event tracking with Headless (Shopify Hydrogen) slug: p3qc0468 canonical_url: https://docs.coveo.com/en/p3qc0468/ collection: coveo-for-commerce source_format: adoc --- # Event tracking with Headless (Shopify Hydrogen) It's crucial that you track [touchpoints](https://docs.coveo.com/en/o6ha0421/) to analyze [storefront](https://docs.coveo.com/en/p33g0410/) performance, create reports, and power [Coveo Machine Learning (Coveo ML)](https://docs.coveo.com/en/188/) [models](https://docs.coveo.com/en/1012/). You can do this by sending events to the [Coveo Analytics](https://docs.coveo.com/en/182/) service. If you use [Coveo Headless](https://docs.coveo.com/en/lcdf0493/) to build your [product discovery solutions](https://docs.coveo.com/en/o9cf0524/), track events using dedicated Headless controllers. These controllers automatically log specific commerce events, such as when a user makes a purchase or adds a product to a cart. Headless commerce controllers rely on the [Event Protocol](https://docs.coveo.com/en/o9je0592/) to log events. The following table shows the types of events to track and the corresponding controllers. [%header,cols="2*"] |=== |Event |Tracking controller |[Click](https://docs.coveo.com/en/o1n92447/) |[Use the `InteractiveProduct` controller to track clicks in product lists](https://docs.coveo.com/en/p24a0093#displaying-lists-of-products) |[Product view](https://docs.coveo.com/en/o1n93101/) |[Use the `ProductView` controller to log view events when a user navigates to a PDP](https://docs.coveo.com/en/p24a0093#displaying-a-product-on-a-product-detail-page) |[Cart](https://docs.coveo.com/en/o1n93466/) |[Use the `Cart` controller to update cart state when products are added or removed](https://docs.coveo.com/en/p1oe0470/) |=== ## Purchase events Purchase events can be tracked automatically by leveraging the Coveo app, which logs [purchase events](https://docs.coveo.com/en/o1n93059/) when the required [cart attributes](https://shopify.dev/docs/storefronts/headless/hydrogen/cart/attributes) are set: - `coveoClientId` - `coveoTrackingId` - `coveoOrganizationId` - `coveoAccessToken` Add the attributes to your `cart` by performing the following series of modifications to your underlying Shopify cart logic. > **Note** > > If you're not using the app, see [implement your own web pixel](https://docs.coveo.com/en/p2la0423#headless) for instructions on how to log purchase events. ### Prerequisites Be familiar with the concepts discussed in [Getting started (Shopify Hydrogen)](https://docs.coveo.com/en/p1oe0067/) and [Core concepts (Shopify Hydrogen)](https://docs.coveo.com/en/p3ae0283/). ### Implementation > **Note** > > Samples in this section are simplified for clarity. > For a complete implementation, see [`($locale).cart.tsx`](https://github.com/coveo-labs/barca-sports-hydrogen/blob/main/app/routes/(%24locale).cart.tsx) in the [Barca Sports demo repository](https://github.com/coveo-labs/barca-sports-hydrogen). The example implementation consists of two main parts, a helper function to update cart attributes and the usage of this helper in both the [`Remix action`](https://v2.remix.run/docs/route/action) and [`Remix loader`](https://v2.remix.run/docs/route/loader/). #### Create a helper function to update the cart Check if the attributes are present on the cart, and if not, add them using the `updateAttributes` method from the `Cart` handler. You'll call this after any operation that retrieves or modifies the cart to configure the attributes as required for proper purchase event tracking. ```typescript // ... import { engineConfig, engineDefinition } from '~/lib/coveo.engine'; // ... async function setCoveoConfigAttributes( context: any, request: Request, cartResult: CartQueryDataReturn, ) { const {cart: cartHandler} = context; const cart = cartResult.cart; if (!cart) return cartResult; const attributes = cart.attributes ?? []; const attributesToUpdate = []; const navigatorProvider = new ServerSideNavigatorContextProvider(request); <1> const {clientId} = navigatorProvider; const { configuration: { analytics: {trackingId}, organizationId, accessToken, }, } = engineConfig; const attributesToFind = [ 'coveoClientId', 'coveoTrackingId', 'coveoOrganizationId', 'coveoAccessToken', ]; const foundAttributes = (cart.attributes ?? []).reduce((acc, item) => { if (attributesToFind.includes(item.key)) { acc[item.key] = {key: item.key, value: item.value}; } return acc; }, {} as Record); if (!foundAttributes.coveoClientId) { attributesToUpdate.push({key: 'coveoClientId', value: clientId}); <2> } if (!foundAttributes.coveoTrackingId) { attributesToUpdate.push({key: 'coveoTrackingId', value: trackingId}); <2> } if (!foundAttributes.coveoOrganizationId) { attributesToUpdate.push({ <2> key: 'coveoOrganizationId', value: organizationId, }); } if (!foundAttributes.coveoAccessToken) { attributesToUpdate.push({key: 'coveoAccessToken', value: accessToken}); <2> } let updatedResult = cartResult; if (attributesToUpdate.length > 0) { try { updatedResult = await cartHandler.updateAttributes(attributesToUpdate); <3> } catch (error) { console.warn('Failed to set Coveo attributes:', error); } } return updatedResult; } ``` <1> Use the `ServerSideNavigatorContextProvider` to obtain the `clientId` for the current session or visitor. For more details on how the `ServerSideNavigatorContextProvider` works and its implementation, see [Server-side and client navigation context providers](https://docs.coveo.com/en/p1oe0067#server-side-and-client-navigation-context-providers). <2> Add the attributes to the list of attributes to update if they're not already present. <3> Update the cart attributes using the [`updateAttributes`](https://shopify.dev/docs/storefronts/headless/hydrogen/cart/attributes#step-2-handle-the-cart-attribute-form-request) method from the `Cart` handler. #### Update the cart attributes Use your helper function when the cart is retrieved and modified. * [`Loader`](https://v2.remix.run/docs/route/loader): on page load to ensure attributes are set when the cart is retrieved. ```typescript export async function loader({request, context}: LoaderFunctionArgs) { const {cart} = context; // ... const cartData = await cart.get(); <1> if (cartData) { await setCoveoConfigAttributes(context, request, {cart: cartData}); <2> } // ... } ``` <1> Fetches the current cart data from Shopify when the page loads or is refreshed. <2> Add the required Coveo cart attributes if they're missing, so purchase events can be tracked correctly. * [`Action`](https://v2.remix.run/docs/route/action/): after any calls that would modify the cart. (for example, adding or removing items) ```typescript export async function action({request, context}: ActionFunctionArgs) { const {cart} = context; // ... result = await setCoveoConfigAttributes(context, request, result); <1> const {cart: cartResult, errors, warnings} = result; return json( { cart: cartResult, errors, warnings, analytics: { cartId, }, }, {status, headers}, ); } ``` <1> Add the required Coveo cart attributes after any cart mutation. This is essential for accurate purchase event tracking by the Coveo app web pixel. By setting the, `coveoCoveoClientId`, `coveoTrackingId`, `coveoOrganizationId` and `coveoAccessToken` attributes on the cart, the Coveo app web pixel can automatically log purchase events when a transaction is completed. This integration streamlines event tracking and analytics for your Shopify with Headless and Hydrogen commerce solution.