Technical reference for implementing Google Analytics 4 (GA4) on Magento 2 (Adobe Commerce), covering the built-in Google API module, custom module development via Layout XML and PHTML templates, RequireJS event binding, and multi-store tracking configuration.
How GA4 Works on Magento
Magento 2 integrates GA4 through its modular PHP architecture. The tracking pipeline works as follows:
- Built-in module (
Magento_GoogleAnalytics): Ships with Magento 2.4+. When enabled via Stores > Configuration > Sales > Google API, the module injectsgtag.jsinto the page head using Layout XML and renders ecommerce events via PHTML templates. The module reads product data from Magento's Block classes ($product->getSku(),$product->getFinalPrice()) at render time and outputs inline<script>blocks. - Custom modules: Magento's Layout XML system (
default.xml,catalog_product_view.xml,checkout_onepage_success.xml) lets you inject tracking scripts into specific pages. Custom Block classes provide server-side access to product collections, quote items, and order data for constructing GA4 event payloads. - AJAX add-to-cart: Magento 2 uses RequireJS and KnockoutJS for frontend interactivity. Add-to-cart actions on category pages fire AJAX requests without full page reloads. Tracking these requires binding to Magento's
ajax:addToCartjQuery event or using KnockoutJS observables.
Magento supports multi-store, multi-website, and multi-currency configurations. Each store view can have a different GA4 Measurement ID configured in Admin. The currency code is resolved per-store via $this->getCurrentCurrencyCode() and must be passed explicitly in every ecommerce event.
Full-page cache (Varnish or built-in) can interfere with dynamic tracking data. Use ESI (Edge Side Includes) or client-side AJAX calls to /customer/section/load/ for customer-specific data that should not be cached.
Installation Methods
Method 1: Magento Admin Configuration (Magento 2.4+)
The built-in Google Analytics integration is the recommended approach.
Step 1: Enable Google Analytics in Magento Admin
- Log in to Magento Admin panel
- Navigate to Stores > Configuration > Sales > Google API
- Expand Google Analytics section
- Set Enable to "Yes"
Step 2: Configure GA4 Settings
- In the Account Number field, enter your GA4 Measurement ID (G-XXXXXXXXXX)
- Configure tracking options:
- Enable Content Experiments: Yes (for A/B testing)
- Enable Enhanced Ecommerce: Yes
- Enable User ID: Yes (for logged-in customers)
- Click Save Config
Step 3: Clear Cache
- Navigate to System > Cache Management
- Click Flush Magento Cache
- Click Flush JavaScript/CSS Cache
Method 2: Custom Implementation via Layout XML
For advanced customization and GA4-specific features.
Step 1: Create Custom Module
Create a custom module structure:
app/code/YourCompany/GA4Tracking/
├── etc/
│ ├── module.xml
│ └── frontend/
│ └── di.xml
├── view/frontend/
│ └── layout/
│ └── default.xml
└── registration.php
Step 2: Add GA4 Tracking to Layout
Create app/code/YourCompany/GA4Tracking/view/frontend/layout/default.xml:
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<head>
<script src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX" async="true"/>
</head>
<body>
<block class="Magento\Framework\View\Element\Template"
name="ga4.tracking"
template="YourCompany_GA4Tracking::ga4.phtml"
after="-"/>
</body>
</page>
Step 3: Create Tracking Template
Create app/code/YourCompany/GA4Tracking/view/frontend/templates/ga4.phtml:
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX', {
'send_page_view': true,
'cookie_flags': 'SameSite=None;Secure'
});
<?php if ($this->getCustomerData()): ?>
// Track logged-in customers
gtag('set', 'user_properties', {
'customer_id': '<?= $this->escapeJs($this->getCustomerId()) ?>',
'customer_group': '<?= $this->escapeJs($this->getCustomerGroup()) ?>'
});
<?php endif; ?>
</script>
Method 3: Google Tag Manager (Recommended for Complex Setups)
For maximum flexibility, use GTM with Magento's data layer.
- Install GTM module from Magento Marketplace
- Configure GTM container ID in Stores > Configuration > General > Google Tag Manager
- Set up GA4 Configuration tag in GTM
- Create ecommerce triggers and variables
Ecommerce Tracking Configuration
Product Impressions and Clicks
Track product views on category and search pages:
<script>
// Product impressions (Category/Search pages)
gtag('event', 'view_item_list', {
'items': [
<?php foreach ($productCollection as $index => $product): ?>
{
'item_id': '<?= $product->getSku() ?>',
'item_name': '<?= $this->escapeJs($product->getName()) ?>',
'item_category': '<?= $this->escapeJs($product->getCategory()) ?>',
'price': <?= $product->getFinalPrice() ?>,
'index': <?= $index ?>,
'quantity': 1
}<?= $index < count($productCollection) - 1 ? ',' : '' ?>
<?php endforeach; ?>
]
});
</script>
Product Detail Views
Track product page views:
<script>
// Product detail view
gtag('event', 'view_item', {
'currency': '<?= $this->getCurrentCurrencyCode() ?>',
'value': <?= $product->getFinalPrice() ?>,
'items': [{
'item_id': '<?= $product->getSku() ?>',
'item_name': '<?= $this->escapeJs($product->getName()) ?>',
'item_brand': '<?= $this->escapeJs($product->getAttributeText("manufacturer")) ?>',
'item_category': '<?= $this->escapeJs($this->getPrimaryCategory()) ?>',
'price': <?= $product->getFinalPrice() ?>
}]
});
</script>
Add to Cart Events
Track add-to-cart actions via AJAX:
<script>
require(['jquery'], function($) {
$(document).on('ajax:addToCart', function(event, data) {
gtag('event', 'add_to_cart', {
'currency': data.currency,
'value': data.price,
'items': [{
'item_id': data.sku,
'item_name': data.name,
'item_category': data.category,
'price': data.price,
'quantity': data.qty
}]
});
});
});
</script>
Checkout Process
Track checkout steps:
<script>
// Begin checkout
gtag('event', 'begin_checkout', {
'currency': '<?= $this->getCurrencyCode() ?>',
'value': <?= $this->getQuote()->getGrandTotal() ?>,
'items': [
<?php foreach ($this->getQuote()->getAllVisibleItems() as $item): ?>
{
'item_id': '<?= $item->getSku() ?>',
'item_name': '<?= $this->escapeJs($item->getName()) ?>',
'price': <?= $item->getPrice() ?>,
'quantity': <?= $item->getQty() ?>
},
<?php endforeach; ?>
]
});
// Add shipping info (shipping step)
gtag('event', 'add_shipping_info', {
'currency': '<?= $this->getCurrencyCode() ?>',
'value': <?= $this->getQuote()->getGrandTotal() ?>,
'shipping_tier': '<?= $this->getShippingMethod() ?>'
});
// Add payment info (payment step)
gtag('event', 'add_payment_info', {
'currency': '<?= $this->getCurrencyCode() ?>',
'value': <?= $this->getQuote()->getGrandTotal() ?>,
'payment_type': '<?= $this->getPaymentMethod() ?>'
});
</script>
Purchase Tracking
Track completed transactions on success page:
<script>
// Purchase event (Success page)
gtag('event', 'purchase', {
'transaction_id': '<?= $this->getOrderId() ?>',
'value': <?= $this->getGrandTotal() ?>,
'tax': <?= $this->getTaxAmount() ?>,
'shipping': <?= $this->getShippingAmount() ?>,
'currency': '<?= $this->getCurrencyCode() ?>',
'coupon': '<?= $this->getCouponCode() ?>',
'items': [
<?php foreach ($this->getOrderItems() as $item): ?>
{
'item_id': '<?= $item->getSku() ?>',
'item_name': '<?= $this->escapeJs($item->getName()) ?>',
'item_category': '<?= $this->escapeJs($item->getCategory()) ?>',
'price': <?= $item->getPrice() ?>,
'quantity': <?= (int)$item->getQtyOrdered() ?>
},
<?php endforeach; ?>
]
});
</script>
Advanced Event Tracking
Wishlist Events
Track wishlist additions:
<script>
require(['jquery'], function($) {
$('.action.towishlist').on('click', function() {
var productData = $(this).data('post');
gtag('event', 'add_to_wishlist', {
'currency': storeCurrency,
'value': productData.price,
'items': [{
'item_id': productData.sku,
'item_name': productData.name,
'price': productData.price
}]
});
});
});
</script>
Product Comparisons
Track product comparison usage:
<script>
gtag('event', 'view_item_list', {
'item_list_name': 'Product Comparison',
'items': comparisonProducts
});
</script>
Search Tracking
Track internal site search:
<script>
<?php if ($this->getSearchQuery()): ?>
gtag('event', 'search', {
'search_term': '<?= $this->escapeJs($this->getSearchQuery()) ?>',
'results_count': <?= $this->getResultCount() ?>
});
<?php endif; ?>
</script>
Review and Rating Events
Track customer reviews:
<script>
$('#review-form').on('submit', function() {
gtag('event', 'review_submit', {
'item_id': productSku,
'item_name': productName,
'rating': $('#rating').val()
});
});
</script>
Customer Tracking
Customer Login
Track customer authentication:
<script>
<?php if ($this->isCustomerLoggedIn()): ?>
gtag('event', 'login', {
'method': 'Magento',
'customer_id': '<?= $this->getCustomerId() ?>'
});
// Set user ID for cross-device tracking
gtag('config', 'G-XXXXXXXXXX', {
'user_id': '<?= $this->getCustomerId() ?>'
});
<?php endif; ?>
</script>
Customer Registration
Track new account creation:
<script>
gtag('event', 'sign_up', {
'method': 'Magento Registration'
});
</script>
Troubleshooting
Tracking Not Working
Issue: No data appearing in GA4
Solutions:
- Clear Magento cache:
php bin/magento cache:clean - Check JavaScript errors in browser console
- Verify Measurement ID format (must start with G-)
- Ensure tracking is enabled in Admin configuration
- Check if ad blockers are interfering
- Verify full-page cache isn't blocking scripts
Duplicate Transactions
Issue: Purchase events firing multiple times
Solutions:
- Ensure tracking code only appears on success page
- Check for page refreshes triggering duplicate events
- Implement transaction deduplication using transaction_id
- Review custom modules for conflicting tracking
- Use cookie-based flag to prevent duplicate fires
Incorrect Product Data
Issue: Product information missing or incorrect
Solutions:
- Verify product attributes are properly configured
- Check SKU field is populated for all products
- Ensure category assignments are correct
- Review product data escaping in templates
- Verify currency code is set correctly
Events Not Firing on AJAX Actions
Issue: Add to cart events not tracking
Solutions:
- Check that jQuery is loaded before tracking code
- Verify event listeners are properly attached
- Use Magento's knockoutJS events for AJAX tracking
- Check browser console for JavaScript errors
- Test with GTM Preview mode for debugging
Multi-Store Tracking Issues
Issue: Data mixed between store views
Solutions:
- Use separate GA4 properties for different stores
- Implement store code as custom dimension
- Filter data in GA4 based on store parameter
- Ensure correct Measurement ID per store view
- Configure view-specific tracking in Magento Admin
Performance Impact
Issue: Site slowdown after GA4 installation
Solutions:
- Enable asynchronous script loading
- Use Google Tag Manager instead of direct implementation
- Implement server-side tracking via Measurement Protocol
- Defer non-critical event tracking
- Enable Magento's JavaScript bundling and minification
- Use Varnish or full-page cache with proper ESI tags
Testing and Verification
Enable GA4 DebugView
Add debug mode to your configuration:
<script>
gtag('config', 'G-XXXXXXXXXX', {
'debug_mode': true
});
</script>
Test Ecommerce Flow
Complete test purchase:
- Add products to cart
- Proceed through checkout
- Complete test order
- Verify all events in GA4 DebugView
- Check purchase appears in Monetization reports
Validate Data Layer
Check Magento's data layer structure:
// In browser console
console.log(window.dataLayer);
Use Tag Assistant
- Install Google Tag Assistant Chrome extension
- Browse your Magento site
- Verify GA4 tags fire correctly
- Check ecommerce parameters are populated
Best Practices
Transaction Deduplication
Prevent duplicate purchase tracking:
<script>
// Set cookie on first success page load
if (!getCookie('order_<?= $orderId ?>_tracked')) {
gtag('event', 'purchase', { /* purchase data */ });
setCookie('order_<?= $orderId ?>_tracked', '1', 1); // 1 day expiry
}
</script>
Server-Side Tracking
For accurate revenue tracking, implement server-side events:
// In Observer or Plugin
$client = new \Google\Analytics\Data\V1beta\AnalyticsDataClient();
// Send server-side purchase event
Multi-Currency Support
Handle multiple currencies correctly:
gtag('event', 'purchase', {
'currency': '<?= $order->getOrderCurrencyCode() ?>',
'value': <?= $order->getGrandTotal() ?>
});