This guide covers how to track custom events and user interactions in Google Analytics 4 on Shopware 6.
For GA4 installation, see Install Google Analytics 4
Shopware Storefront Event System
Shopware 6 provides a built-in event system for the storefront that you can leverage for tracking.
Available Storefront Events
Shopware's JavaScript event system uses a publish/subscribe pattern:
// Subscribe to events
document.$emitter.subscribe('eventName', function(event) {
// Handle event
});
// Publish events
document.$emitter.publish('eventName', { data: 'value' });
Common Shopware Events
| Event | Trigger | Data Available |
|---|---|---|
offcanvas-cart-opened |
Cart drawer opened | Cart data |
offcanvas-cart-closed |
Cart drawer closed | - |
ajax-offcanvas-cart-finished |
Cart updated via AJAX | Cart contents |
Listing/afterRenderFilters |
Product filters applied | Filter values |
ProductDetail/afterRenderWishlist |
Product added to wishlist | Product data |
Tracking Standard GA4 Events
Page View Tracking
Page views are automatically tracked if configured in GA4 setup:
gtag('config', 'G-XXXXXXXXXX', {
'send_page_view': true
});
For single-page app behavior or AJAX navigation:
// Track virtual page views
gtag('event', 'page_view', {
'page_title': document.title,
'page_location': window.location.href,
'page_path': window.location.pathname
});
Search Tracking
Track search queries when users search your store:
{% block base_header_search_suggest %}
{{ parent() }}
<script>
// Listen for search form submission
document.addEventListener('DOMContentLoaded', function() {
const searchForm = document.querySelector('.header-search-form');
if (searchForm) {
searchForm.addEventListener('submit', function(e) {
const searchInput = searchForm.querySelector('input[name="search"]');
const searchTerm = searchInput ? searchInput.value : '';
if (searchTerm && typeof gtag !== 'undefined') {
gtag('event', 'search', {
'search_term': searchTerm
});
}
});
}
});
</script>
{% endblock %}
Filter Usage Tracking
Track when users apply product filters:
// Custom JavaScript plugin or theme.js
document.$emitter.subscribe('Listing/afterRenderFilters', function(event) {
const filters = event.detail;
if (typeof gtag !== 'undefined') {
gtag('event', 'filter_products', {
'filter_type': filters.type || 'unknown',
'filter_value': filters.value || '',
'category': filters.category || 'all'
});
}
});
Wishlist Tracking
Track when products are added to wishlist:
{% block page_product_detail_wishlist %}
{{ parent() }}
<script>
document.addEventListener('DOMContentLoaded', function() {
const wishlistButton = document.querySelector('[data-wishlist-add-button]');
if (wishlistButton) {
wishlistButton.addEventListener('click', function() {
const productId = this.dataset.productId;
const productName = this.dataset.productName || 'Unknown';
if (typeof gtag !== 'undefined') {
gtag('event', 'add_to_wishlist', {
'items': [{
'item_id': productId,
'item_name': productName
}]
});
}
});
}
});
</script>
{% endblock %}
Newsletter Signup Tracking
Track newsletter subscriptions:
{% block footer_newsletter_form %}
{{ parent() %}
<script>
document.addEventListener('DOMContentLoaded', function() {
const newsletterForm = document.querySelector('.footer-newsletter-form');
if (newsletterForm) {
newsletterForm.addEventListener('submit', function(e) {
if (typeof gtag !== 'undefined') {
gtag('event', 'newsletter_signup', {
'method': 'footer_form',
'location': 'footer'
});
}
});
}
});
</script>
{% endblock %}
Custom Event Tracking
Button Click Tracking
Track specific button clicks:
// Add to custom JavaScript file
document.addEventListener('DOMContentLoaded', function() {
// Track CTA button clicks
document.querySelectorAll('[data-track-click]').forEach(function(button) {
button.addEventListener('click', function() {
const eventName = this.dataset.trackClick;
const eventCategory = this.dataset.trackCategory || 'engagement';
const eventLabel = this.dataset.trackLabel || this.textContent;
if (typeof gtag !== 'undefined') {
gtag('event', eventName, {
'event_category': eventCategory,
'event_label': eventLabel
});
}
});
});
});
Usage in Twig:
<button
data-track-click="view_size_guide"
data-track-category="product_interaction"
data-track-label="Size Guide Button">
View Size Guide
</button>
Link Click Tracking
Track outbound links:
// Track external link clicks
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('a[href^="http"]').forEach(function(link) {
if (!link.href.includes(window.location.hostname)) {
link.addEventListener('click', function(e) {
const destination = this.href;
if (typeof gtag !== 'undefined') {
gtag('event', 'click', {
'event_category': 'outbound_link',
'event_label': destination,
'transport_type': 'beacon'
});
}
});
}
});
});
File Download Tracking
Track PDF downloads and other files:
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('a[href$=".pdf"], a[href$=".zip"], a[href$=".doc"]').forEach(function(link) {
link.addEventListener('click', function(e) {
const filePath = this.href;
const fileName = filePath.split('/').pop();
const fileType = fileName.split('.').pop();
if (typeof gtag !== 'undefined') {
gtag('event', 'file_download', {
'file_name': fileName,
'file_extension': fileType,
'link_url': filePath
});
}
});
});
});
Video Interaction Tracking
Track video plays (for embedded videos):
// For HTML5 videos
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('video').forEach(function(video) {
// Track play
video.addEventListener('play', function() {
if (typeof gtag !== 'undefined') {
gtag('event', 'video_start', {
'video_title': this.title || 'Unknown',
'video_url': this.currentSrc
});
}
});
// Track completion
video.addEventListener('ended', function() {
if (typeof gtag !== 'undefined') {
gtag('event', 'video_complete', {
'video_title': this.title || 'Unknown',
'video_url': this.currentSrc
});
}
});
// Track progress (25%, 50%, 75%)
const progressPoints = [0.25, 0.5, 0.75];
const tracked = new Set();
video.addEventListener('timeupdate', function() {
const progress = this.currentTime / this.duration;
progressPoints.forEach(function(point) {
if (progress >= point && !tracked.has(point)) {
tracked.add(point);
if (typeof gtag !== 'undefined') {
gtag('event', 'video_progress', {
'video_title': video.title || 'Unknown',
'video_percent': Math.round(point * 100)
});
}
}
});
});
});
});
User Engagement Events
Scroll Depth Tracking
Track how far users scroll on pages:
// Scroll depth tracking
(function() {
const scrollThresholds = [25, 50, 75, 100];
const trackedThresholds = new Set();
window.addEventListener('scroll', function() {
const scrollPercent = (window.scrollY + window.innerHeight) / document.body.scrollHeight * 100;
scrollThresholds.forEach(function(threshold) {
if (scrollPercent >= threshold && !trackedThresholds.has(threshold)) {
trackedThresholds.add(threshold);
if (typeof gtag !== 'undefined') {
gtag('event', 'scroll', {
'event_category': 'engagement',
'event_label': threshold + '%',
'value': threshold
});
}
}
});
});
})();
Time on Page
Track engaged time on page:
// Track engaged time (user is active)
(function() {
let startTime = Date.now();
let isActive = true;
let totalEngagedTime = 0;
// Track user activity
['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart'].forEach(function(event) {
document.addEventListener(event, function() {
isActive = true;
}, { passive: true });
});
// Check engagement every second
setInterval(function() {
if (isActive) {
totalEngagedTime += 1;
isActive = false;
}
}, 1000);
// Send engaged time when leaving page
window.addEventListener('beforeunload', function() {
if (typeof gtag !== 'undefined' && totalEngagedTime > 0) {
gtag('event', 'user_engagement', {
'engagement_time_msec': totalEngagedTime * 1000,
'page_path': window.location.pathname
});
}
});
})();
Enhanced Measurement Events
Form Interactions
Track form starts and submissions:
// Track form interactions
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('form').forEach(function(form) {
let formStarted = false;
// Track form start (first interaction)
form.addEventListener('focus', function(e) {
if (!formStarted && e.target.matches('input, textarea, select')) {
formStarted = true;
if (typeof gtag !== 'undefined') {
gtag('event', 'form_start', {
'form_id': form.id || 'unnamed',
'form_name': form.name || 'unnamed'
});
}
}
}, true);
// Track form submission
form.addEventListener('submit', function() {
if (typeof gtag !== 'undefined') {
gtag('event', 'form_submit', {
'form_id': form.id || 'unnamed',
'form_name': form.name || 'unnamed'
});
}
});
});
});
Account Events
Track account-related actions:
{# In account login template #}
{% block page_account_login_form %}
{{ parent() }}
<script>
document.querySelector('.login-form')?.addEventListener('submit', function() {
if (typeof gtag !== 'undefined') {
gtag('event', 'login', {
'method': 'email'
});
}
});
</script>
{% endblock %}
{# In account registration template #}
{% block page_account_register_form %}
{{ parent() }}
<script>
document.querySelector('.register-form')?.addEventListener('submit', function() {
if (typeof gtag !== 'undefined') {
gtag('event', 'sign_up', {
'method': 'email'
});
}
});
</script>
{% endblock %}
Using GTM for Event Tracking
For more flexible event tracking, use Google Tag Manager:
1. Set Up GTM
See Install Google Tag Manager
2. Push Events to Data Layer
// Instead of gtag(), push to dataLayer
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'event': 'customEvent',
'eventCategory': 'category',
'eventAction': 'action',
'eventLabel': 'label',
'eventValue': 123
});
3. Create Tags in GTM
- Trigger: Custom Event (event equals customEvent)
- Tag: GA4 Event
- Event Name: From variable
\{\{eventAction\}\} - Parameters: Map from data layer variables
Event Parameters Best Practices
Recommended Parameters
Standard Parameters:
item_id: Product/content identifieritem_name: Human-readable namevalue: Monetary value (for conversions)currency: ISO currency codemethod: How the action was performed
Custom Parameters:
user_type: "customer" or "guest"sales_channel: Sales channel IDcustomer_group: Customer group namelanguage: Current languagedevice_type: Mobile, tablet, desktop
Parameter Naming Convention
Use snake_case for consistency with GA4:
// Good
gtag('event', 'custom_event', {
'event_category': 'engagement',
'product_category': 'shoes'
});
// Avoid
gtag('event', 'customEvent', {
'EventCategory': 'engagement',
'ProductCategory': 'shoes'
});
Debugging Event Tracking
Browser Console
Check if events are firing:
// Monitor gtag calls
window.gtag = new Proxy(window.gtag || function(){}, {
apply: function(target, thisArg, argumentsList) {
console.log('gtag called:', argumentsList);
return target.apply(thisArg, argumentsList);
}
});
GA4 DebugView
- Enable debug mode:
gtag('config', 'G-XXXXXXXXXX', {
'debug_mode': true
});
- View in GA4: Admin → DebugView
GTM Preview Mode
If using GTM:
- Open GTM → Preview
- Enter your Shopware store URL
- Navigate and trigger events
- View events in Tag Assistant
Common Issues
Events not showing:
- Check cookie consent is granted
- Verify GA4 Measurement ID is correct
- Check JavaScript console for errors
- Clear Shopware cache
- Verify gtag is defined
Duplicate events:
- Check for multiple implementations
- Verify event is only triggered once
- Use event listeners with
{ once: true }
Performance Considerations
Debounce High-Frequency Events
For events that fire frequently (scroll, mouse move):
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// Use debounced scroll
const trackScroll = debounce(function() {
gtag('event', 'scroll', { /* ... */ });
}, 500);
window.addEventListener('scroll', trackScroll);
Limit Event Volume
Set reasonable limits on tracking:
- Don't track every mouse movement
- Limit scroll depth to key thresholds
- Batch similar events when possible
- Use sampling for high-traffic sites
Next Steps
- Set up Ecommerce Tracking - Track products and purchases
- Troubleshoot Events - Fix tracking issues
- GTM Data Layer - Use Shopware data with GTM
For general GA4 event concepts, see Google Analytics 4 Event Tracking.