Overview
Event tracking in GA4 captures user interactions beyond pageviews, including:
- Button clicks (Add to Cart, Wishlist, Compare)
- Form submissions (Newsletter signup, Contact forms)
- Downloads and file interactions
- Video plays and engagement
- Custom user actions
Event Tracking Methods
Method 1: Extension-Based Events
Most premium GA4 extensions automatically track common events:
Auto-Tracked Events (Typical Extensions)
add_to_cart- Product added to cartremove_from_cart- Product removed from cartview_item- Product detail viewadd_to_wishlist- Product added to wishlistbegin_checkout- Checkout process startedadd_payment_info- Payment method selectedadd_shipping_info- Shipping method selectedpurchase- Order completedsearch- Site search performedselect_item- Product clicked from listing
Configuration:
Admin Panel > Extensions > Extensions > Analytics > [Your GA4 Extension] > Edit
Enable desired auto-tracking features and save.
Method 2: Custom JavaScript Events
For tracking custom interactions not covered by extensions:
1. Add to Cart Button Tracking
Create a new JavaScript file or add to existing theme JS:
File: catalog/view/theme/[your-theme]/template/product/product.twig
Add before the closing </body> tag or in a separate JS file:
<script>
// Track Add to Cart button clicks
$(document).on('click', '#button-cart', function() {
// Get product data from page
var productName = $('h1').first().text().trim();
var productId = $('input[name="product_id"]').val();
var quantity = $('input[name="quantity"]').val();
var price = $('.price-new').text() || $('.price').text();
// Remove currency symbols and convert to number
var priceValue = parseFloat(price.replace(/[^0-9.]/g, ''));
// Send event to GA4
gtag('event', 'add_to_cart', {
'currency': 'USD',
'value': priceValue * quantity,
'items': [{
'item_id': productId,
'item_name': productName,
'quantity': quantity,
'price': priceValue
}]
});
});
</script>
2. Newsletter Subscription Tracking
File: catalog/view/theme/[your-theme]/template/common/footer.twig
<script>
// Track newsletter subscriptions
$(document).on('click', '#newsletter-subscribe', function() {
var email = $('input[name="newsletter_email"]').val();
gtag('event', 'newsletter_signup', {
'method': 'footer_form',
'email_provided': email ? 'yes' : 'no'
});
});
</script>
3. Product Wishlist Tracking
<script>
// Track wishlist additions
$(document).on('click', '.wishlist-button, button[onclick*="wishlist.add"]', function() {
var productId = $(this).data('product-id') ||
$(this).attr('onclick').match(/\d+/)[0];
gtag('event', 'add_to_wishlist', {
'items': [{
'item_id': productId
}]
});
});
</script>
4. Product Compare Tracking
<script>
// Track product compare additions
$(document).on('click', '.compare-button, button[onclick*="compare.add"]', function() {
var productId = $(this).data('product-id') ||
$(this).attr('onclick').match(/\d+/)[0];
gtag('event', 'add_to_compare', {
'items': [{
'item_id': productId
}]
});
});
</script>
5. Contact Form Tracking
File: catalog/view/theme/[your-theme]/template/information/contact.twig
<script>
// Track contact form submissions
$(document).on('submit', '#contact-form', function() {
gtag('event', 'contact_form_submit', {
'form_type': 'contact',
'form_location': 'contact_page'
});
});
</script>
Method 3: Controller-Based Event Tracking
For server-side event tracking or when you need access to PHP data:
1. Create Custom Controller Extension
File: catalog/controller/extension/analytics/ga4_events.php
<?php
class ControllerExtensionAnalyticsGa4Events extends Controller {
public function index() {
// Only load if GA4 is enabled
if ($this->config->get('analytics_ga4_status')) {
$this->load->model('extension/analytics/ga4_events');
// Get GA4 Measurement ID
$measurement_id = $this->config->get('analytics_ga4_measurement_id');
// Return tracking code with events
return $this->model_extension_analytics_ga4_events->getEventScript($measurement_id);
}
return '';
}
public function trackCheckoutStep() {
// Called via AJAX during checkout
if ($this->request->server['REQUEST_METHOD'] == 'POST') {
$step = isset($this->request->post['step']) ? $this->request->post['step'] : '';
$event_data = array(
'event' => 'checkout_progress',
'checkout_step' => $step,
'checkout_option' => $this->getCheckoutOption($step)
);
$json['success'] = true;
$json['event_data'] = $event_data;
$this->response->addHeader('Content-Type: application/json');
$this->response->setOutput(json_encode($json));
}
}
private function getCheckoutOption($step) {
$options = array(
'payment_address' => 'Billing Address',
'shipping_address' => 'Shipping Address',
'shipping_method' => 'Shipping Method',
'payment_method' => 'Payment Method',
'confirm' => 'Order Confirmation'
);
return isset($options[$step]) ? $options[$step] : '';
}
}
2. Create Model for Event Data
File: catalog/model/extension/analytics/ga4_events.php
<?php
class ModelExtensionAnalyticsGa4Events extends Model {
public function getEventScript($measurement_id) {
$data = array();
$data['measurement_id'] = $measurement_id;
// Get cart data for events
$data['cart_items'] = $this->getCartItems();
return $this->load->view('extension/analytics/ga4_events', $data);
}
private function getCartItems() {
$items = array();
$products = $this->cart->getProducts();
foreach ($products as $product) {
$items[] = array(
'item_id' => $product['product_id'],
'item_name' => $product['name'],
'price' => $this->currency->format($product['price'], $this->session->data['currency'], '', false),
'quantity' => $product['quantity']
);
}
return $items;
}
public function getProductData($product_id) {
$this->load->model('catalog/product');
$product = $this->model_catalog_product->getProduct($product_id);
if ($product) {
return array(
'item_id' => $product['product_id'],
'item_name' => $product['name'],
'item_category' => $this->getProductCategory($product_id),
'price' => $this->currency->format($product['price'], $this->session->data['currency'], '', false)
);
}
return array();
}
private function getProductCategory($product_id) {
$this->load->model('catalog/product');
$categories = $this->model_catalog_product->getCategories($product_id);
if ($categories) {
$this->load->model('catalog/category');
$category = $this->model_catalog_category->getCategory($categories[0]['category_id']);
return $category ? $category['name'] : '';
}
return '';
}
}
Method 4: OCMOD for Event Injection
Create an OCMOD to automatically inject event tracking across your store:
File: ga4-events.ocmod.xml
<?xml version="1.0" encoding="utf-8"?>
<modification>
<name>GA4 Event Tracking</name>
<code>ga4_event_tracking</code>
<version>1.0</version>
<author>Your Name</author>
<!-- Add to Cart Event -->
<file path="catalog/view/theme/*/template/product/product.twig">
<operation>
<search><![CDATA[<button type="button" id="button-cart"]]></search>
<add position="before"><![CDATA[
<script>
$(document).ready(function() {
$('#button-cart').on('click', function() {
gtag('event', 'add_to_cart', {
'currency': '{{ currency_code }}',
'value': {{ product_price }},
'items': [{
'item_id': '{{ product_id }}',
'item_name': '{{ product_name }}',
'price': {{ product_price }}
}]
});
});
});
</script>
]]></add>
</operation>
</file>
<!-- Search Event -->
<file path="catalog/view/theme/*/template/common/search.twig">
<operation>
<search><![CDATA[<button type="submit"]]></search>
<add position="before"><![CDATA[
<script>
$(document).ready(function() {
$('#search input[name="search"]').closest('form').on('submit', function() {
var searchTerm = $(this).find('input[name="search"]').val();
gtag('event', 'search', {
'search_term': searchTerm
});
});
});
</script>
]]></add>
</operation>
</file>
</modification>
Install OCMOD:
Admin Panel > Extensions > Installer > Upload
Admin Panel > Extensions > Modifications > Refresh
Event Data Layer Integration
For advanced implementations, create a data layer that feeds GA4:
File: catalog/view/theme/[your-theme]/template/common/header.twig
Add after opening <body> tag:
<script>
window.dataLayer = window.dataLayer || [];
{% if page_type == 'product' %}
dataLayer.push({
'event': 'view_item',
'ecommerce': {
'items': [{
'item_id': '{{ product_id }}',
'item_name': '{{ product_name }}',
'item_category': '{{ product_category }}',
'price': {{ product_price }},
'currency': '{{ currency_code }}'
}]
}
});
{% endif %}
{% if page_type == 'category' %}
dataLayer.push({
'event': 'view_item_list',
'ecommerce': {
'items': [
{% for product in products %}
{
'item_id': '{{ product.product_id }}',
'item_name': '{{ product.name }}',
'price': {{ product.price }},
'index': {{ loop.index }}
}{% if not loop.last %},{% endif %}
{% endfor %}
]
}
});
{% endif %}
</script>
Custom Event Parameters
User Properties
Track user-specific properties:
gtag('set', 'user_properties', {
'customer_type': 'returning', // or 'new'
'customer_group': 'wholesale', // or 'retail'
'customer_lifetime_value': 1500.00
});
Enhanced Event Data
Add rich context to events:
gtag('event', 'view_item', {
'currency': 'USD',
'value': 29.99,
'items': [{
'item_id': '12345',
'item_name': 'Product Name',
'item_category': 'Category',
'item_category2': 'Subcategory',
'item_variant': 'Blue',
'item_brand': 'Brand Name',
'price': 29.99,
'quantity': 1
}],
// Custom parameters
'stock_level': 'in_stock',
'product_rating': 4.5,
'review_count': 87
});
Testing Events
Using GA4 DebugView
- Enable Debug Mode
Add to your page:
gtag('config', 'G-XXXXXXXXXX', {
'debug_mode': true
});
Or use Chrome extension: Google Analytics Debugger
- View Events in GA4
- Go to GA4 property
- Navigate to Configure > DebugView
- Interact with your store in another tab
- Watch events appear in real-time
Browser Console Testing
Test events directly in browser console:
// Test add_to_cart event
gtag('event', 'add_to_cart', {
'currency': 'USD',
'value': 29.99,
'items': [{
'item_id': 'TEST123',
'item_name': 'Test Product',
'price': 29.99
}]
});
Network Tab Verification
- Open Developer Tools (F12)
- Go to Network tab
- Filter by "collect"
- Trigger an event (e.g., add to cart)
- Click the request to
/g/collect - Verify event parameters in Payload tab
Common Event Issues
Events Not Firing
Symptoms: Events don't appear in GA4 DebugView
Solutions:
Check GA4 code is loaded:
console.log(typeof gtag); // Should output "function"Verify Measurement ID is correct
Check for JavaScript errors in console
Ensure event is triggered (add console.log before gtag call)
Duplicate Events
Symptoms: Same event fires multiple times
Solutions:
- Use event delegation instead of multiple bindings
- Add
.off()before.on()to prevent double-binding:$(document).off('click', '#button-cart').on('click', '#button-cart', function() { // Event code }); - Check for multiple GA4 extensions enabled
Missing Event Parameters
Symptoms: Events fire but lack required parameters
Solutions:
- Verify data availability:
console.log('Product ID:', productId); console.log('Price:', priceValue); - Check Twig variable output
- Ensure proper data type (number vs string)