view_item
The view_item event fires when a user views a product detail page. This is your product demand signal — it tells you which products get attention, how variant selection affects engagement, and where in the funnel users drop off before adding to cart.
view_item is the destination event for select_item. The item data you push here should be the most complete version of the product record you have: full category hierarchy, brand, variant, and price. The detail page is where you have all this data available, so use it.
When to fire
Section titled “When to fire”Fire view_item when:
- The product detail page loads (for traditional multi-page sites)
- The product detail view renders in a SPA (after component mount, not on route change)
- A quick-view modal opens with product detail content
Fire it once per product view, not on every render. For SPAs, use a ref guard or check to prevent re-firing on component re-renders.
Complete dataLayer push
Section titled “Complete dataLayer push”// Always clear previous ecommerce data firstdataLayer.push({ ecommerce: null });
dataLayer.push({ event: 'view_item', ecommerce: { currency: 'USD', value: 89.99, items: [ { item_id: 'SKU-001-BLK', item_name: 'Classic Leather Jacket', item_brand: 'Heritage Co.', item_category: 'Apparel', item_category2: 'Outerwear', item_category3: 'Jackets', item_category4: 'Leather', item_variant: 'Black / Large', price: 89.99, quantity: 1, index: 0, affiliation: 'Online Store', coupon: '', discount: 0, // List context — preserve from select_item if navigated from a list item_list_id: 'mens_outerwear', item_list_name: "Men's Outerwear" } ] }});The value on view_item is the price of the product being viewed — the same value as items[0].price. For a standard single-product view, value = price.
Event schema
Section titled “Event schema”| Parameter | Type | Required | Description |
|---|---|---|---|
| event | string | Required | Must be "view_item" |
| ecommerce.currency | string | Required | ISO 4217 currency code. Required for GA4 to attribute monetary value. |
| ecommerce.value | number | Required | Monetary value — the product price. Must be a number. |
| ecommerce.items[] | Array<Item> | Required | Array containing one item — the product being viewed. |
| items[].item_id | string | Required | SKU or unique product identifier. |
| items[].item_name | string | Required | Product name. |
| items[].item_brand | string | Optional | Brand or manufacturer. |
| items[].item_category | string | Optional | Primary product category. |
| items[].item_category2 | string | Optional | Second-level category. |
| items[].item_category3 | string | Optional | Third-level category. |
| items[].item_category4 | string | Optional | Fourth-level category. |
| items[].item_variant | string | Optional | Currently selected variant (color, size, style). Update when user changes variant selection. |
| items[].price | number | Optional | Current price of the selected variant. Must match the ecommerce.value. |
| items[].quantity | number | Optional | Always 1 for view_item. |
| items[].affiliation | string | Optional | Store affiliation for marketplace scenarios. |
| items[].coupon | string | Optional | Active coupon code if one is applied to this item. |
| items[].discount | number | Optional | Discount amount if item is on sale. |
| items[].item_list_id | string | Optional | The list the user navigated from — preserves funnel path context. |
| items[].item_list_name | string | Optional | Human-readable name of the list the user navigated from. |
Handling variant selection
Section titled “Handling variant selection”When a product has variants (color, size, material) and the user changes the selected variant, you have two options:
Option A: Fire once on page load with the default variant. Simpler to implement. Loses the information about which variant the user actually considered.
Option B: Fire again when the variant selection changes. More work, but captures variant-level demand signal. Which color is most viewed? Which size runs out of stock because it’s most desired?
// Option B: re-fire when variant changesfunction trackVariantView(product, selectedVariant) { window.dataLayer = window.dataLayer || []; window.dataLayer.push({ ecommerce: null }); window.dataLayer.push({ event: 'view_item', ecommerce: { currency: product.currency, value: selectedVariant.price, items: [{ item_id: selectedVariant.sku, // variant-level SKU item_name: product.name, item_brand: product.brand, item_category: product.category, item_variant: selectedVariant.label, // e.g., 'Navy / Medium' price: selectedVariant.price, quantity: 1 }] } });}
// Fire on page load with default varianttrackVariantView(product, product.defaultVariant);
// Re-fire when user changes variantcolorSelector.addEventListener('change', () => { const variant = getSelectedVariant(); trackVariantView(product, variant);});Preserving list context
Section titled “Preserving list context”If the user navigated to this product detail page from a list (via select_item), carry the list context forward in item_list_id and item_list_name. This enables GA4 to build path reports showing “users who viewed items from Men’s Outerwear are X% more likely to purchase.”
For server-rendered pages, pass the list context via a query parameter or session storage, and read it back on the product page.
// On select_item (list page) — store context for the product pagesessionStorage.setItem('last_item_list', JSON.stringify({ item_list_id: 'mens_outerwear', item_list_name: "Men's Outerwear"}));
// On view_item (product detail page) — restore contextconst listContext = JSON.parse(sessionStorage.getItem('last_item_list') || '{}');
dataLayer.push({ ecommerce: null });dataLayer.push({ event: 'view_item', ecommerce: { currency: 'USD', value: 89.99, items: [{ item_id: 'SKU-001-BLK', item_name: 'Classic Leather Jacket', price: 89.99, quantity: 1, item_list_id: listContext.item_list_id || '', item_list_name: listContext.item_list_name || '' }] }});GTM configuration
Section titled “GTM configuration”-
Create a Custom Event trigger. Event name:
view_item. Name itCE - view_item. -
Create Data Layer Variables. Variables for
ecommerce.currency,ecommerce.value, andecommerce.items. -
Create the GA4 Event tag. Event name:
view_item. Enable Send Ecommerce data. Addcurrencyandvalueas explicit event parameters (they don’t flow through the ecommerce checkbox automatically). -
Test in Preview mode. Load a product page. Confirm one
view_itemfires with all parameters populated.
Common mistakes
Section titled “Common mistakes”Firing on component re-render in SPAs. In React, a useEffect without a ref guard fires on every render. Wrap the push in a useRef(false) guard, or use useEffect with a dependency array that only changes when the product ID changes.
Sending the cart quantity instead of 1. view_item describes viewing the product, not purchasing a quantity. quantity: 1 on this event. The actual purchase quantity goes on add_to_cart and purchase.
Missing currency. Without currency, GA4 ignores the value parameter entirely. Your product performance reports will show views but zero revenue contribution.
Not updating the push when variant changes. If you implement Option A and fire only on page load, accept that you’re losing variant-level demand data. Don’t half-implement Option B by firing the initial push with a default variant but never re-firing — that’s worse than Option A because it looks like you have variant data but it’s unreliable.