You can integrate Google Tag Manager with your Merchandising Cloud implementation to track product click events and impressions, enabling data-driven merchandising decisions based on customer behavior. This integration helps you measure search performance, understand which products resonate with customers, and identify optimization opportunities that can improve conversion rates.
Setting up Google Tag Manager tracking for product interactions provides valuable insights into how customers engage with your search results, collections, and recommendations. This data helps you optimize product positioning, refine merchandising strategies, and measure the effectiveness of your ecommerce search and discovery features.
Time Estimates
- Set up in Platform: n/a hours
- Integration: 1.5 hours
- Styling: n/a
Functional Overview
Integration Steps
1. Product Click Events.
In order to send Product Click events to Google Analytics, you need to override the default Findify onClick function in 'components/Cards/Product/index.tsx' and create a 'sendFindifyClickEvent' function:
components/Cards/Product/index.tsx
/** * @module components /Cards/Product */import cx from 'classnames';import trackProductPosition from 'helpers/trackProductPosition';const sendFindifyClickEvent = (list, item, config, index) => new Promise(resolve => window.dataLayer.push({ eventCallback: resolve, event: "gaEvent", eventCategory: "Ecommerce", eventAction: "Product Click", ecommerce: { click: { actionField: { list }, products: [{ base_product: item.get('item_group_id'), variant: item.get('selected_variant_id'), name: item.get('title'), // Name or ID is required. id: item.get('id'), price: item.getIn(['price', 0]), brand: item.get('brand'), category: item.getIn(['category', 0, 'category1']), list: list, position: index + 1 }] } } }));export default ({ index, item, theme = styles, className, config, highlighted, isAutocomplete, isSearch, isCollection, isRecommendation}: IProductCardProps) => { const container = trackProductPosition(item); const [variant, setVariant] = useVariants(item); let list = ''; switch(true){ case isAutocomplete: list = 'Autocomplete Results' break; case isCollection: list = 'Collection Results' break; case isRecommendation: list = 'Recommendation' break; default: list = 'Search Results'; } const itemOnclick = (e) => { const openInNewWindow = e && (e.ctrlKey || e.metaKey); const productUrl = item.get('product_url'); item.analytics.sendEvent( 'click-item', { rid: item.meta.get('rid'), item_id: item.get('id'), variant_item_id: item.get('selected_variant_id')}, !openInNewWindow ); if (isSearch && !openInNewWindow && typeof window !== 'undefined') { document.location.hash = item.get('id'); } sendFindifyClickEvent(list, item, config, index); } return ( <a ref={container} data-element="card" className={cx( theme.root, theme[config.get('template')], highlighted && theme.highlighted, isAutocomplete && theme.autocomplete, className )} href={url} onClick={itemOnclick} > // default code </a> );};
2. Product Impressions.
If you need to send Product Impressions to StaticResults (or LazyResults), Recommendations and Autocomplete, this is what the build looks like:
components/search/LazyResultslayouts/Recommendation/Slider/index.tsxcomponents/autocomplete/Products/index.tsx
/** * @module components /search/LazyResults */import {useEffect} from 'react';import { useConfig } from '@findify /react-connect';import styles from 'components/search/LazyResults/styles.css';import useLazy from 'helpers/useLazy';// default codeconst pushDataLayers = (data, config) => window.dataLayer && data && window.dataLayer.push({ event: "data.pageView", ecommerce: { currencyCode: config.getIn(['currency', 'code']), impressions: data.toJS() }});const pushImpressions = (item, index, listType) => ({ base_product: item.get('item_group_id'), variant: item.get('selected_variant_id'), name: item.get('title'), // Name or ID is required. id: item.get('id'), price: item.getIn(['price', 0]), brand: item.get('brand'), category: item.getIn(['category', 0, 'category1']), list: listType, position: index + 1});const sendGAEvents = (items, config, meta, isCollection) => { const offset = meta.get('offset'); const limit = meta.get('limit'); const listType = isCollection ? 'Collection Results' : 'Search Results'; const itemsCutLimit = items.slice(offset, offset + limit); const itemsImpressions = itemsCutLimit.map((item, index) => pushImpressions(item, offset + index, listType)); if(itemsImpressions.size > 0){ pushDataLayers(itemsImpressions, config, isCollection); }}const checkIfTheSameProducts = (currItems) => { const lastDataLayer = dataLayer.filter(i => i.event === 'data.pageView').pop(); const check = lastDataLayer && lastDataLayer.ecommerce.impressions.length === currItems.size && lastDataLayer.ecommerce.impressions.filter(i => currItems.find(currItem => currItem.get('title') === i.name)); return check && check.length === currItems.size;}const LazyResults = ({ theme = styles, card = ProductCard, itemConfig, isMobile, isCollection, meta }: ILazyResultsProps) => { const { // default code items } = useLazy(); const { config } = useConfig<Immutable.SearchConfig>();// default code useEffect( () => { if(!checkIfTheSameProducts(items)){ sendGAEvents(items, config, meta, isCollection); } }, [items] ); return ( // default code );};export default LazyResults;
MJS Version
This module has been optimized for MJS version 7.1.27
Related Articles
Integration & Setup:
Search & Discovery:
Product Recommendations: