select_item
The select_item event fires when a user clicks on a product in a list to navigate to the product detail page. Its primary purpose is to carry the list context — which list the user came from, what position the product held — from the product listing page through to the product detail page and eventually to add_to_cart and purchase.
Without select_item, you can measure list impressions (view_item_list) and product detail views (view_item), but you cannot close the loop between them. You cannot answer “which list sends the most engaged buyers” because you have no record of the path from list to detail page.
When to fire
Section titled “When to fire”Fire select_item when:
- A user clicks a product card in a category list, search results, or recommendation widget
- A user clicks a product in a “related products” or “customers also bought” widget
- A user selects a product from an autocomplete search suggestion list
Fire it on the source page (where the list is), not on the destination page. The event fires before navigation — on click, not on page load.
Complete dataLayer push
Section titled “Complete dataLayer push”// On click of a product in a listdataLayer.push({ ecommerce: null });
dataLayer.push({ event: 'select_item', ecommerce: { item_list_id: 'mens_outerwear', item_list_name: "Men's Outerwear", 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_variant: 'Black', price: 89.99, quantity: 1, index: 0, item_list_id: 'mens_outerwear', item_list_name: "Men's Outerwear" } ] }});The items array for select_item contains exactly one item — the product that was clicked. This is in contrast to view_item_list, which contains all visible products.
Event schema
Section titled “Event schema”| Parameter | Type | Required | Description |
|---|---|---|---|
| event | string | Required | Must be "select_item" |
| ecommerce.item_list_id | string | Optional | The list identifier from the view_item_list event for this list. Must match exactly. |
| ecommerce.item_list_name | string | Optional | The human-readable list name from the view_item_list event. Must match exactly. |
| ecommerce.items[] | Array<Item> | Required | Array containing exactly one item — the product that was clicked. |
| 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_variant | string | Optional | Variant shown in the list at time of click. |
| items[].price | number | Optional | Listed price at time of click. |
| items[].quantity | number | Optional | Always 1 for select_item. |
| items[].index | number | Optional | Position in the list (0-based). Must match the index from view_item_list. |
| items[].item_list_id | string | Optional | Same as ecommerce.item_list_id — repeat at item level. |
| items[].item_list_name | string | Optional | Same as ecommerce.item_list_name — repeat at item level. |
Implementation patterns
Section titled “Implementation patterns”Reading list context from rendered product cards
Section titled “Reading list context from rendered product cards”The most reliable implementation stores the list context in data attributes on the product card HTML, then reads those attributes on click.
// HTML — server-rendered product card// <div class="product-card"// data-item-id="SKU-001-BLK"// data-item-name="Classic Leather Jacket"// data-item-price="89.99"// data-item-list-id="mens_outerwear"// data-item-list-name="Men's Outerwear"// data-index="0">
document.querySelectorAll('.product-card').forEach(card => { card.addEventListener('click', () => { const d = card.dataset;
dataLayer.push({ ecommerce: null }); dataLayer.push({ event: 'select_item', ecommerce: { item_list_id: d.itemListId, item_list_name: d.itemListName, items: [{ item_id: d.itemId, item_name: d.itemName, price: parseFloat(d.itemPrice), quantity: 1, index: parseInt(d.index, 10), item_list_id: d.itemListId, item_list_name: d.itemListName }] } }); });});React component pattern
Section titled “React component pattern”interface ProductCardProps { product: Product; listId: string; listName: string; index: number;}
function ProductCard({ product, listId, listName, index }: ProductCardProps) { function handleClick() { window.dataLayer = window.dataLayer || []; window.dataLayer.push({ ecommerce: null }); window.dataLayer.push({ event: 'select_item', ecommerce: { item_list_id: listId, item_list_name: listName, items: [{ item_id: product.sku, item_name: product.name, item_brand: product.brand, item_category: product.category, item_variant: product.defaultVariant, price: product.price, quantity: 1, index, item_list_id: listId, item_list_name: listName }] } }); }
return ( <a href={product.url} onClick={handleClick}> <img src={product.image} alt={product.name} /> <h3>{product.name}</h3> <span>{product.formattedPrice}</span> </a> );}GTM configuration
Section titled “GTM configuration”-
Create a Custom Event trigger. Event name:
select_item. Name itCE - select_item. -
Create Data Layer Variables. Variables for
ecommerce.items(the full array),ecommerce.item_list_id, andecommerce.item_list_name. -
Create the GA4 Event tag. Event name:
select_item. Enable Send Ecommerce data. Optionally additem_list_idanditem_list_nameas additional event parameters for list-level filtering in GA4. -
Test in Preview mode. Click a product. Confirm the event fires with one item in the items array, and that
indexmatches the product’s position.
Common mistakes
Section titled “Common mistakes”Missing list context. The entire point of select_item is to carry list context forward. If item_list_id and item_list_name are missing or don’t match what was pushed on view_item_list, the funnel path data is broken.
Including multiple items. select_item describes a single product click. The items array should contain exactly one item. Including the whole list is wrong.
Firing on page load instead of on click. This event fires on the source page when the user interacts, not on the destination page when it loads. Firing it on the product detail page misses the list context entirely.
Not preserving the index. If your product cards are rendered dynamically and you don’t track position, you lose the ability to analyze how product position affects click-through rate. Always store index and include it in the push.