view_promotion / select_promotion
Promotion tracking measures the performance of internal marketing — your homepage hero banners, seasonal sale modules, featured collection widgets, and “top picks” recommendations. view_promotion fires when a promotion is visible to the user. select_promotion fires when they click it.
Together, these two events give you impression-to-click rates for every internal promotion. Which banner drives the most clicks? Which creative slot position performs best? Promotion tracking answers these without needing a separate A/B testing tool for basic performance measurement.
view_promotion: when to fire
Section titled “view_promotion: when to fire”Fire view_promotion when:
- A promotional banner, hero image, or featured content block becomes visible to the user
- A recommendation widget with promotional intent renders on the page
- A “Featured Products” or “Sale” module is in the viewport
Like view_item_list, fire once per promotion render — not on scroll events. If you have multiple promotions on a page, fire a separate event for each promotion (or batch multiple promotions in one event if they all appear simultaneously).
view_promotion: complete dataLayer push
Section titled “view_promotion: complete dataLayer push”// Multiple promotions visible on the homepagedataLayer.push({ ecommerce: null });
dataLayer.push({ event: 'view_promotion', ecommerce: { items: [ { item_id: 'SKU-001-BLK', item_name: 'Classic Leather Jacket', item_brand: 'Heritage Co.', item_category: 'Apparel', promotion_id: 'PROMO_SUMMER24', promotion_name: 'Summer Sale 2024', creative_name: 'summer_sale_hero_v2', creative_slot: 'homepage_hero' }, { item_id: 'SKU-023-NAV', item_name: 'Wool Peacoat', item_brand: 'Heritage Co.', item_category: 'Apparel', promotion_id: 'PROMO_NEW_ARRIVALS', promotion_name: 'New Arrivals - Fall Collection', creative_name: 'new_arrivals_banner_a', creative_slot: 'homepage_secondary' }, { item_id: 'SKU-088-BLK', item_name: 'Leather Card Wallet', item_brand: 'Heritage Co.', item_category: 'Accessories', promotion_id: 'PROMO_ACCESSORIES', promotion_name: 'Accessories Sale', creative_name: 'accessories_grid_module', creative_slot: 'homepage_tertiary' } ] }});select_promotion: when to fire
Section titled “select_promotion: when to fire”Fire select_promotion when:
- A user clicks on a promotional banner, hero image, or featured module
- A user clicks a “Shop Now” CTA within a promotion
- A user clicks a featured product within a promotional widget
select_promotion should fire on the source page (where the promotion is), before navigation to the destination.
select_promotion: complete dataLayer push
Section titled “select_promotion: complete dataLayer push”// User clicks the summer sale hero bannerdataLayer.push({ ecommerce: null });
dataLayer.push({ event: 'select_promotion', ecommerce: { items: [ { item_id: 'SKU-001-BLK', item_name: 'Classic Leather Jacket', item_brand: 'Heritage Co.', item_category: 'Apparel', promotion_id: 'PROMO_SUMMER24', promotion_name: 'Summer Sale 2024', creative_name: 'summer_sale_hero_v2', creative_slot: 'homepage_hero' } ] }});Event schema
Section titled “Event schema”| Parameter | Type | Required | Description |
|---|---|---|---|
| event | string | Required | "view_promotion" or "select_promotion" |
| ecommerce.items[] | Array<Item> | Required | Items associated with the promotion. Each item carries promotion metadata. |
| items[].item_id | string | Optional | SKU of the featured product, if the promotion is product-specific. |
| items[].item_name | string | Optional | Name of the featured product. |
| items[].promotion_id | string | Optional | Unique identifier for the promotion campaign. e.g., PROMO_SUMMER24. |
| items[].promotion_name | string | Optional | Human-readable promotion name. e.g., "Summer Sale 2024". |
| items[].creative_name | string | Optional | The specific creative asset name. Use versioning: hero_banner_v1 vs hero_banner_v2. |
| items[].creative_slot | string | Optional | The position or slot where the creative appears. e.g., homepage_hero, pdp_sidebar. |
| items[].index | number | Optional | Position of the promotion in a list of promotions (0-based). |
Promotion parameters explained
Section titled “Promotion parameters explained”promotion_id vs. promotion_name
Section titled “promotion_id vs. promotion_name”promotion_id is the machine-readable identifier — what your CMS or campaign management tool uses internally. promotion_name is what humans call the campaign. Both should be consistent throughout the campaign lifecycle.
promotion_id: 'SUMMER2024_SALE',promotion_name: 'Summer Sale 2024'creative_name: version your creatives
Section titled “creative_name: version your creatives”When you A/B test banner designs or run multiple creative versions of the same campaign, use creative_name to distinguish them. This is where the value of promotion tracking becomes clear — you can compare click rates between hero_v1 and hero_v2 in GA4 reports.
creative_name: 'summer_sale_hero_v1' // controlcreative_name: 'summer_sale_hero_v2' // test variantcreative_slot: the position identifier
Section titled “creative_slot: the position identifier”creative_slot identifies where on the page the promotion lives, independent of what’s in it. Slots are positions — the hero slot, the secondary banner slot, the sidebar widget slot. The same slot might run different promotions over time, but its position in the page layout stays constant.
creative_slot: 'homepage_hero'creative_slot: 'homepage_secondary_banner'creative_slot: 'category_page_top'creative_slot: 'pdp_related_products'creative_slot: 'cart_cross_sell'GTM configuration
Section titled “GTM configuration”-
Create two Custom Event triggers.
CE - view_promotionforview_promotion, andCE - select_promotionforselect_promotion. -
Create Data Layer Variables for
ecommerce.items(shared between both events). -
Create two GA4 Event tags — one for each event name. Both use Send Ecommerce data.
-
Test in Preview mode. Navigate to a page with promotions. Confirm
view_promotionfires when the promotion is visible. Click a promotion and confirmselect_promotionfires with matching promotion parameters.
Common mistakes
Section titled “Common mistakes”Not linking view to click with consistent IDs. If view_promotion pushes promotion_id: 'PROMO_SUMMER24' but select_promotion pushes promotion_id: 'promo_summer24' (different case) or an entirely different ID, GA4 cannot connect the view to the click. Be exact.
Tracking image loads as promotions. Not every hero image is a promotion. view_promotion is for deliberate marketing content — campaigns with defined objectives. A decorative image is not a promotion.
Using the same creative_slot for different positions. If your “homepage hero” slot sometimes renders full-width and sometimes half-width depending on the campaign, use distinct slot names: homepage_hero_fullwidth vs homepage_hero_halfwidth. Mixing layouts under one slot name makes position analysis meaningless.