5. Listening to store events
In some cases, the service provided by the third party solution depends on store Events to properly work.
We call Events or Pixel Events the notifications automatically broadcasted by a website whenever users perform critical actions in your store, such as adding products to the cart or even product page viewing.
For this scenario, it is fundamental to develop a script whose aim is to listen to the desired store Events in order to send the needed data to the third party solution.
- Access your app's
react/index.tsx
file. - Write your script according to your Pixel app needs. For example:
import { canUseDOM } from 'vtex.render-runtime'
import push from './modules/push'
import {
Order,
PixelMessage,
ProductOrder,
Impression,
CartItem,
} from './typings/events'
import { AnalyticsEcommerceProduct } from './typings/gtm'
export default function() {
return null
} // no-op for extension point
export function handleEvents(e: PixelMessage) {
switch (e.data.eventName) {
case 'vtex:pageView': {
push({
event: 'pageView',
location: e.data.pageUrl,
page: e.data.pageUrl.replace(e.origin, ''),
referrer: e.data.referrer,
...(e.data.pageTitle && {
title: e.data.pageTitle,
}),
})
return
}
case 'vtex:productView': {
const { selectedSku, productName, brand, categories } = e.data.product
let price
try {
price = e.data.product.items[0].sellers[0].commertialOffer.Price
} catch {
price = undefined
}
const data = {
ecommerce: {
detail: {
products: [
{
brand,
category: getCategory(categories),
id: selectedSku.itemId,
name: productName,
variant: selectedSku.name,
price,
},
],
},
},
event: 'productDetail',
}
push(data)
return
}
case 'vtex:productClick': {
const { productName, brand, categories, sku } = e.data.product
let price
try {
price = e.data.product.items[0].sellers[0].commertialOffer.Price
} catch {
price = undefined
}
const data = {
event: 'productClick',
ecommerce: {
click: {
products: [
{
brand,
category: getCategory(categories),
id: sku.itemId,
name: productName,
variant: sku.name,
price,
},
],
},
},
}
push(data)
return
}
case 'vtex:addToCart': {
const { items } = e.data
push({
ecommerce: {
add: {
products: items.map((sku: any) => ({
brand: sku.brand,
category: sku.category,
id: sku.skuId,
name: sku.name,
price: `${sku.price}`,
quantity: sku.quantity,
variant: sku.variant,
})),
},
currencyCode: e.data.currency,
},
event: 'addToCart',
})
return
}
case 'vtex:removeFromCart': {
const { items } = e.data
push({
ecommerce: {
currencyCode: e.data.currency,
remove: {
products: items.map((sku: any) => ({
brand: sku.brand,
id: sku.skuId,
category: sku.category,
name: sku.name,
price: `${sku.price}`,
quantity: sku.quantity,
variant: sku.variant,
})),
},
},
event: 'removeFromCart',
})
return
}
case 'vtex:orderPlaced': {
const order = e.data
const ecommerce = {
purchase: {
actionField: getPurchaseObjectData(order),
products: order.transactionProducts.map((product: ProductOrder) =>
getProductObjectData(product)
),
},
}
push({
// @ts-ignore
event: 'orderPlaced',
...order,
ecommerce,
})
// Backwards compatible event
push({
ecommerce,
event: 'pageLoaded',
})
return
}
case 'vtex:productImpression': {
const { currency, list, impressions, product, position } = e.data
let oldImpresionFormat: Record<string, any> | null = null
if (product != null && position != null) {
// make it backwards compatible
oldImpresionFormat = [
getProductImpressionObjectData(list)({
product,
position,
}),
]
}
const parsedImpressions = (impressions || []).map(
getProductImpressionObjectData(list)
)
push({
event: 'productImpression',
ecommerce: {
currencyCode: currency,
impressions: oldImpresionFormat || parsedImpressions,
},
})
return
}
case 'vtex:userData': {
const { data } = e
if (!data.isAuthenticated) {
return
}
push({
event: 'userData',
userId: data.id,
})
return
}
case 'vtex:cartLoaded': {
const { orderForm } = e.data
push({
event: 'checkout',
ecommerce: {
checkout: {
actionField: {
step: 1,
},
products: orderForm.items.map(getCheckoutProductObjectData),
},
},
})
break
}
default: {
break
}
}
}
function getPurchaseObjectData(order: Order) {
return {
affiliation: order.transactionAffiliation,
coupon: order.coupon ? order.coupon : null,
id: order.orderGroup,
revenue: order.transactionTotal,
shipping: order.transactionShipping,
tax: order.transactionTax,
}
}
function getProductObjectData(product: ProductOrder) {
return {
brand: product.brand,
category: product.categoryTree?.join('/'),
id: product.sku,
name: product.name,
price: product.price,
quantity: product.quantity,
variant: product.skuName,
}
}
function getCategory(rawCategories: string[]) {
if (!rawCategories || !rawCategories.length) {
return
}
return removeStartAndEndSlash(rawCategories[0])
}
// Transform this: "/Apparel & Accessories/Clothing/Tops/"
// To this: "Apparel & Accessories/Clothing/Tops"
function removeStartAndEndSlash(category?: string) {
return category?.replace(/^\/|\/$/g, '')
}
function getProductImpressionObjectData(list: string) {
return ({ product, position }: Impression) => ({
brand: product.brand,
category: getCategory(product.categories),
id: product.sku.itemId,
list,
name: product.productName,
position,
price: `${product.sku.seller!.commertialOffer.Price}`,
variant: product.sku.name,
})
}
function getCheckoutProductObjectData(
item: CartItem
): AnalyticsEcommerceProduct {
return {
id: item.id,
name: item.name,
category: Object.keys(item.productCategories ?? {}).reduce(
(categories, category) =>
categories ? `${categories}/${category}` : category,
''
),
brand: item.additionalInfo?.brandName ?? '',
variant: item.skuName,
price: item.sellingPrice / 100,
quantity: item.quantity,
}
}
if (canUseDOM) {
window.addEventListener('message', handleEvents)
}
This is an example of an event implementation in our Google Tag Manager Pixel app.
Some store events you can work with in your Pixel app are, namely:
addToCart
- Triggered when a product is added to the cart.removeItem
- Triggered when a product is removed from the cartpageView
- Triggered on every loaded page view.productImpression
- Triggered when product data is visible on the page currently being accessed by users.
All the available event properties are typed in [TypeScript] - go through them here.
Updated 1 day ago
Did this page help you?