Documentation
Feedback
Guides
App Development

App Development
Getting StartedPixel apps5. Listening to store events
5. Listening to store events

Some services provided by third-party solutions may rely on store events. Events are notifications a web application automatically broadcasts whenever a user performs an important action in a store, such as adding items to the shopping cart or accessing a product page.

In these scenarios, your Pixel app must be able to listen to the desired events and provide the required data to the third-party service in question.

Instructions

  1. Open the react/index.tsx file.
  2. Write your script based on your Pixel app needs. Some store events you can use in your Pixel app are:
  • addToCart - Triggered when a product is added to the cart.
  • removeFromCart - Triggered when a product is removed from the cart.
  • pageView - Triggered on every loaded page view.
  • productImpression - Triggered when product information is visible on the page currently being accessed by users.

All the available event properties are written in TypeScript. For a comprehensive list of properties, please refer to this file.

This is an example of an event implementation in the Google Tag Manager Pixel app:


_315
import { canUseDOM } from 'vtex.render-runtime'
_315
_315
import push from './modules/push'
_315
import {
_315
Order,
_315
PixelMessage,
_315
ProductOrder,
_315
Impression,
_315
CartItem,
_315
} from './typings/events'
_315
import { AnalyticsEcommerceProduct } from './typings/gtm'
_315
_315
export default function() {
_315
return null
_315
} // no-op for extension point
_315
_315
export function handleEvents(e: PixelMessage) {
_315
switch (e.data.eventName) {
_315
case 'vtex:pageView': {
_315
push({
_315
event: 'pageView',
_315
location: e.data.pageUrl,
_315
page: e.data.pageUrl.replace(e.origin, ''),
_315
referrer: e.data.referrer,
_315
...(e.data.pageTitle && {
_315
title: e.data.pageTitle,
_315
}),
_315
})
_315
_315
return
_315
}
_315
_315
case 'vtex:productView': {
_315
const { selectedSku, productName, brand, categories } = e.data.product
_315
_315
let price
_315
_315
try {
_315
price = e.data.product.items[0].sellers[0].commertialOffer.Price
_315
} catch {
_315
price = undefined
_315
}
_315
_315
const data = {
_315
ecommerce: {
_315
detail: {
_315
products: [
_315
{
_315
brand,
_315
category: getCategory(categories),
_315
id: selectedSku.itemId,
_315
name: productName,
_315
variant: selectedSku.name,
_315
price,
_315
},
_315
],
_315
},
_315
},
_315
event: 'productDetail',
_315
}
_315
_315
push(data)
_315
_315
return
_315
}
_315
_315
case 'vtex:productClick': {
_315
const { productName, brand, categories, sku } = e.data.product
_315
_315
let price
_315
_315
try {
_315
price = e.data.product.items[0].sellers[0].commertialOffer.Price
_315
} catch {
_315
price = undefined
_315
}
_315
_315
const data = {
_315
event: 'productClick',
_315
ecommerce: {
_315
click: {
_315
products: [
_315
{
_315
brand,
_315
category: getCategory(categories),
_315
id: sku.itemId,
_315
name: productName,
_315
variant: sku.name,
_315
price,
_315
},
_315
],
_315
},
_315
},
_315
}
_315
_315
push(data)
_315
_315
return
_315
}
_315
_315
case 'vtex:addToCart': {
_315
const { items } = e.data
_315
_315
push({
_315
ecommerce: {
_315
add: {
_315
products: items.map((sku: any) => ({
_315
brand: sku.brand,
_315
category: sku.category,
_315
id: sku.skuId,
_315
name: sku.name,
_315
price: `$\{sku.price\}`,
_315
quantity: sku.quantity,
_315
variant: sku.variant,
_315
})),
_315
},
_315
currencyCode: e.data.currency,
_315
},
_315
event: 'addToCart',
_315
})
_315
_315
return
_315
}
_315
_315
case 'vtex:removeFromCart': {
_315
const { items } = e.data
_315
_315
push({
_315
ecommerce: {
_315
currencyCode: e.data.currency,
_315
remove: {
_315
products: items.map((sku: any) => ({
_315
brand: sku.brand,
_315
id: sku.skuId,
_315
category: sku.category,
_315
name: sku.name,
_315
price: `$\{sku.price\}`,
_315
quantity: sku.quantity,
_315
variant: sku.variant,
_315
})),
_315
},
_315
},
_315
event: 'removeFromCart',
_315
})
_315
_315
return
_315
}
_315
_315
case 'vtex:orderPlaced': {
_315
const order = e.data
_315
_315
const ecommerce = {
_315
purchase: {
_315
actionField: getPurchaseObjectData(order),
_315
products: order.transactionProducts.map((product: ProductOrder) =>
_315
getProductObjectData(product)
_315
),
_315
},
_315
}
_315
_315
push({
_315
// @ts-ignore
_315
event: 'orderPlaced',
_315
...order,
_315
ecommerce,
_315
})
_315
_315
// Backwards compatible event
_315
push({
_315
ecommerce,
_315
event: 'pageLoaded',
_315
})
_315
_315
return
_315
}
_315
_315
case 'vtex:productImpression': {
_315
const { currency, list, impressions, product, position } = e.data
_315
let oldImpresionFormat: Record<string, any> | null = null
_315
_315
if (product != null && position != null) {
_315
// make it backwards compatible
_315
oldImpresionFormat = [
_315
getProductImpressionObjectData(list)({
_315
product,
_315
position,
_315
}),
_315
]
_315
}
_315
_315
const parsedImpressions = (impressions || []).map(
_315
getProductImpressionObjectData(list)
_315
)
_315
_315
push({
_315
event: 'productImpression',
_315
ecommerce: {
_315
currencyCode: currency,
_315
impressions: oldImpresionFormat || parsedImpressions,
_315
},
_315
})
_315
_315
return
_315
}
_315
_315
case 'vtex:userData': {
_315
const { data } = e
_315
_315
if (!data.isAuthenticated) {
_315
return
_315
}
_315
_315
push({
_315
event: 'userData',
_315
userId: data.id,
_315
})
_315
_315
return
_315
}
_315
_315
case 'vtex:cartLoaded': {
_315
const { orderForm } = e.data
_315
_315
push({
_315
event: 'checkout',
_315
ecommerce: {
_315
checkout: {
_315
actionField: {
_315
step: 1,
_315
},
_315
products: orderForm.items.map(getCheckoutProductObjectData),
_315
},
_315
},
_315
})
_315
_315
break
_315
}
_315
_315
default: {
_315
break
_315
}
_315
}
_315
}
_315
_315
function getPurchaseObjectData(order: Order) {
_315
return {
_315
affiliation: order.transactionAffiliation,
_315
coupon: order.coupon ? order.coupon : null,
_315
id: order.orderGroup,
_315
revenue: order.transactionTotal,
_315
shipping: order.transactionShipping,
_315
tax: order.transactionTax,
_315
}
_315
}
_315
_315
function getProductObjectData(product: ProductOrder) {
_315
return {
_315
brand: product.brand,
_315
category: product.categoryTree?.join('/'),
_315
id: product.sku,
_315
name: product.name,
_315
price: product.price,
_315
quantity: product.quantity,
_315
variant: product.skuName,
_315
}
_315
}
_315
_315
function getCategory(rawCategories: string[]) {
_315
if (!rawCategories || !rawCategories.length) {
_315
return
_315
}
_315
_315
return removeStartAndEndSlash(rawCategories[0])
_315
}
_315
_315
// Transform this: "/Apparel & Accessories/Clothing/Tops/"
_315
// To this: "Apparel & Accessories/Clothing/Tops"
_315
function removeStartAndEndSlash(category?: string) {
_315
return category?.replace(/^\/|\/$/g, '')
_315
}
_315
_315
function getProductImpressionObjectData(list: string) {
_315
return ({ product, position }: Impression) => ({
_315
brand: product.brand,
_315
category: getCategory(product.categories),
_315
id: product.sku.itemId,
_315
list,
_315
name: product.productName,
_315
position,
_315
price: `$\{product.sku.seller!.commertialOffer.Price\}`,
_315
variant: product.sku.name,
_315
})
_315
}
_315
_315
function getCheckoutProductObjectData(
_315
item: CartItem
_315
): AnalyticsEcommerceProduct {
_315
return {
_315
id: item.id,
_315
name: item.name,
_315
category: Object.keys(item.productCategories ?? {}).reduce(
_315
(categories, category) =>
_315
categories ? `$\{categories\}/$\{category\}` : category,
_315
''
_315
),
_315
brand: item.additionalInfo?.brandName ?? '',
_315
variant: item.skuName,
_315
price: item.sellingPrice / 100,
_315
quantity: item.quantity,
_315
}
_315
}
_315
_315
if (canUseDOM) {
_315
window.addEventListener('message', handleEvents)
_315
}

Contributors
1
Photo of the contributor
+ 1 contributors
Was this helpful?
Yes
No
Suggest Edits (GitHub)
See also
6. Structuring the documentation
App Development
Contributors
1
Photo of the contributor
+ 1 contributors
On this page