Initial commit
This commit is contained in:
572
skills/gtm-datalayer/references/datalayer-best-practices.md
Normal file
572
skills/gtm-datalayer/references/datalayer-best-practices.md
Normal file
@@ -0,0 +1,572 @@
|
||||
# Data Layer Best Practices
|
||||
|
||||
## Overview
|
||||
|
||||
The data layer is a JavaScript object used by Google Tag Manager and gtag.js to pass information to tags. Events or variables can be passed via the data layer, and triggers can be set up based on the values of variables. This guide covers best practices for implementing and maintaining the data layer.
|
||||
|
||||
## Installation and Setup
|
||||
|
||||
### Proper Data Layer Initialization
|
||||
|
||||
The data layer must be established before the Tag Manager or Google tag snippet loads:
|
||||
|
||||
```html
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
</script>
|
||||
<!-- Google Tag Manager -->
|
||||
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
||||
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
||||
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
||||
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
||||
})(window,document,'script','dataLayer','GTM-XXXXXX');</script>
|
||||
<!-- End Google Tag Manager -->
|
||||
```
|
||||
|
||||
### Gtag.js Installation
|
||||
|
||||
For gtag.js implementations:
|
||||
|
||||
```html
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=TAG_ID"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments)};
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', 'TAG_ID');
|
||||
</script>
|
||||
```
|
||||
|
||||
## How Data Layer Information is Processed
|
||||
|
||||
Tag Manager processes data layer messages on a first-in, first-out (FIFO) basis:
|
||||
|
||||
1. Each message is processed one at a time, in the order received
|
||||
2. If a message is an event, any tags with trigger conditions that have been met will fire before Tag Manager moves to the next message
|
||||
3. `gtag()` or `dataLayer.push()` calls queue messages for processing after all pending messages
|
||||
4. Updated data layer values are not guaranteed to be available for the next event
|
||||
|
||||
### Event Handling Pattern
|
||||
|
||||
To ensure data availability, add an event name to messages and listen for that event with a Custom Event trigger:
|
||||
|
||||
```javascript
|
||||
// Push data with a custom event
|
||||
dataLayer.push({
|
||||
'event': 'userData',
|
||||
'userId': '12345',
|
||||
'userType': 'premium'
|
||||
});
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Never Overwrite the dataLayer Variable
|
||||
|
||||
**Bad:**
|
||||
|
||||
```javascript
|
||||
// This overwrites existing values!
|
||||
dataLayer = [{'item': 'value'}];
|
||||
```
|
||||
|
||||
**Good:**
|
||||
|
||||
```javascript
|
||||
// Initialize at the top of the page
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
|
||||
// Then use push() to add values
|
||||
dataLayer.push({'item': 'value'});
|
||||
```
|
||||
|
||||
### 2. Use Correct Casing
|
||||
|
||||
The `dataLayer` object name is case-sensitive:
|
||||
|
||||
**Bad:**
|
||||
|
||||
```javascript
|
||||
datalayer.push({'pageTitle': 'Home'}); // lowercase 'l'
|
||||
DataLayer.push({'pageTitle': 'Home'}); // capital 'D'
|
||||
```
|
||||
|
||||
**Good:**
|
||||
|
||||
```javascript
|
||||
dataLayer.push({'pageTitle': 'Home'}); // correct camelCase
|
||||
```
|
||||
|
||||
### 3. Use Proper Quote Marks
|
||||
|
||||
All data layer variable names should be enclosed in quotes:
|
||||
|
||||
**Bad:**
|
||||
|
||||
```javascript
|
||||
dataLayer.push({new-variable: 'value'}); // No quotes
|
||||
dataLayer.push({newVariable: 'value'}); // No quotes (though valid JS)
|
||||
```
|
||||
|
||||
**Good:**
|
||||
|
||||
```javascript
|
||||
dataLayer.push({'new-variable': 'value'}); // Proper quotes
|
||||
dataLayer.push({'newVariable': 'value'}); // Proper quotes
|
||||
```
|
||||
|
||||
### 4. Keep Variable Names Consistent Across Pages
|
||||
|
||||
Use consistent naming conventions for the same concept across all pages:
|
||||
|
||||
**Bad:**
|
||||
|
||||
```javascript
|
||||
// Homepage:
|
||||
dataLayer.push({'visitorType': 'low-value'});
|
||||
|
||||
// Checkout Page:
|
||||
dataLayer.push({'visitor_type': 'high-value'}); // Different naming!
|
||||
```
|
||||
|
||||
**Good:**
|
||||
|
||||
```javascript
|
||||
// Homepage:
|
||||
dataLayer.push({'visitorType': 'low-value'});
|
||||
|
||||
// Checkout Page:
|
||||
dataLayer.push({'visitorType': 'high-value'}); // Consistent naming
|
||||
```
|
||||
|
||||
## Using the Data Layer with Event Handlers
|
||||
|
||||
### Sending Events
|
||||
|
||||
Use the `event` key to initiate sending events:
|
||||
|
||||
```javascript
|
||||
dataLayer.push({'event': 'event_name'});
|
||||
```
|
||||
|
||||
### Event with Button Click
|
||||
|
||||
```html
|
||||
<button onclick="dataLayer.push({'event': 'login'});">Login</button>
|
||||
```
|
||||
|
||||
### Dynamic Data Layer Variables
|
||||
|
||||
Push variables dynamically to capture information:
|
||||
|
||||
```javascript
|
||||
dataLayer.push({'variable_name': 'variable_value'});
|
||||
```
|
||||
|
||||
#### Example: Form Value Capture
|
||||
|
||||
```javascript
|
||||
// Capture color selection
|
||||
dataLayer.push({'color': 'red'});
|
||||
```
|
||||
|
||||
## Advanced Patterns
|
||||
|
||||
### Pushing Multiple Variables and Events
|
||||
|
||||
You can push multiple variables and events simultaneously:
|
||||
|
||||
```javascript
|
||||
dataLayer.push({
|
||||
'color': 'red',
|
||||
'conversionValue': 50,
|
||||
'event': 'customize'
|
||||
});
|
||||
```
|
||||
|
||||
### Persisting Data Layer Variables Across Pages
|
||||
|
||||
To persist data layer variables between pages:
|
||||
|
||||
1. Call `dataLayer.push()` on each page load after data layer instantiation
|
||||
2. Place the push above the GTM container code for immediate availability
|
||||
|
||||
```html
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
|
||||
dataLayer.push({
|
||||
'event': 'Pageview',
|
||||
'pagePath': 'https://www.example.com/products',
|
||||
'pageTitle': 'Product Catalog',
|
||||
'visitorType': 'customer'
|
||||
});
|
||||
</script>
|
||||
<!-- Google Tag Manager -->
|
||||
<script>(function(w,d,s,l,i){...})(window,document,'script','dataLayer','GTM-XXXXXX');</script>
|
||||
<!-- End Google Tag Manager -->
|
||||
```
|
||||
|
||||
**Important:** Variables persist only as long as the visitor remains on the current page. Variables relevant across pages must be declared in the data layer on each page.
|
||||
|
||||
## Custom Data Layer Methods
|
||||
|
||||
### The set() Method
|
||||
|
||||
Set values to retrieve later:
|
||||
|
||||
```javascript
|
||||
window.dataLayer.push(function() {
|
||||
this.set('time', new Date());
|
||||
});
|
||||
```
|
||||
|
||||
### The get() Method
|
||||
|
||||
Retrieve values that were set:
|
||||
|
||||
```javascript
|
||||
window.dataLayer.push(function() {
|
||||
const existingTime = this.get('time');
|
||||
if (existingTime !== null) {
|
||||
// Value exists, use it
|
||||
} else {
|
||||
// Value doesn't exist
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### The reset() Method
|
||||
|
||||
Reset the data in the data layer (useful for single-page applications):
|
||||
|
||||
```javascript
|
||||
window.dataLayer.push(function() {
|
||||
this.reset();
|
||||
});
|
||||
```
|
||||
|
||||
## Renaming the Data Layer
|
||||
|
||||
### For gtag.js
|
||||
|
||||
Add a query parameter named "l" to set a new data layer name:
|
||||
|
||||
```html
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=TAG_ID&l=myNewName"></script>
|
||||
<script>
|
||||
window.myNewName = window.myNewName || [];
|
||||
function gtag(){myNewName.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', 'TAG_ID');
|
||||
</script>
|
||||
```
|
||||
|
||||
### For Tag Manager
|
||||
|
||||
Replace the data layer parameter value in the container snippet:
|
||||
|
||||
```html
|
||||
<!-- Google Tag Manager -->
|
||||
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
||||
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
||||
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
||||
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
||||
})(window,document,'script','myNewName','GTM-XXXXXX');</script>
|
||||
<!-- End Google Tag Manager -->
|
||||
```
|
||||
|
||||
Update all references to match the new name:
|
||||
|
||||
```javascript
|
||||
<script>
|
||||
myNewName = window.myNewName || [];
|
||||
myNewName.push({'variable_name': 'variable_value'});
|
||||
</script>
|
||||
```
|
||||
|
||||
## Common Data Layer Patterns
|
||||
|
||||
### Page View Data
|
||||
|
||||
```javascript
|
||||
dataLayer.push({
|
||||
'event': 'pageview',
|
||||
'page': {
|
||||
'path': '/products/shoes',
|
||||
'title': 'Shoes | My Store',
|
||||
'category': 'Products'
|
||||
},
|
||||
'user': {
|
||||
'id': 'USER123',
|
||||
'status': 'logged_in',
|
||||
'type': 'premium'
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### User Interaction
|
||||
|
||||
```javascript
|
||||
dataLayer.push({
|
||||
'event': 'button_click',
|
||||
'element': {
|
||||
'id': 'cta-button',
|
||||
'text': 'Get Started',
|
||||
'destination': '/signup'
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Form Submission
|
||||
|
||||
```javascript
|
||||
dataLayer.push({
|
||||
'event': 'form_submission',
|
||||
'form': {
|
||||
'id': 'contact-form',
|
||||
'name': 'Contact Us',
|
||||
'fields': 5
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Video Tracking
|
||||
|
||||
```javascript
|
||||
dataLayer.push({
|
||||
'event': 'video_start',
|
||||
'video': {
|
||||
'title': 'Product Demo',
|
||||
'duration': 120,
|
||||
'provider': 'youtube',
|
||||
'url': 'https://youtube.com/watch?v=xxx'
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Errors
|
||||
|
||||
#### 1. Overwriting dataLayer
|
||||
|
||||
**Problem:** Using direct assignment instead of push
|
||||
|
||||
```javascript
|
||||
// Wrong
|
||||
dataLayer = [{'item': 'value'}];
|
||||
```
|
||||
|
||||
**Solution:** Always use push after initialization
|
||||
|
||||
```javascript
|
||||
// Correct
|
||||
dataLayer.push({'item': 'value'});
|
||||
```
|
||||
|
||||
#### 2. Case Sensitivity
|
||||
|
||||
**Problem:** Incorrect casing
|
||||
|
||||
```javascript
|
||||
datalayer.push({'pageTitle': 'Home'}); // Wrong
|
||||
```
|
||||
|
||||
**Solution:** Use correct camelCase
|
||||
|
||||
```javascript
|
||||
dataLayer.push({'pageTitle': 'Home'}); // Correct
|
||||
```
|
||||
|
||||
#### 3. Missing Quotes
|
||||
|
||||
**Problem:** Variable names without quotes
|
||||
|
||||
```javascript
|
||||
dataLayer.push({pageTitle: 'Home'}); // Can cause issues
|
||||
```
|
||||
|
||||
**Solution:** Always use quotes
|
||||
|
||||
```javascript
|
||||
dataLayer.push({'pageTitle': 'Home'}); // Better
|
||||
```
|
||||
|
||||
#### 4. Inconsistent Variable Names
|
||||
|
||||
**Problem:** Using different names for the same concept
|
||||
|
||||
```javascript
|
||||
// Page 1
|
||||
dataLayer.push({'user_type': 'premium'});
|
||||
|
||||
// Page 2
|
||||
dataLayer.push({'userType': 'premium'});
|
||||
```
|
||||
|
||||
**Solution:** Document and enforce naming conventions
|
||||
|
||||
```javascript
|
||||
// All pages
|
||||
dataLayer.push({'userType': 'premium'});
|
||||
```
|
||||
|
||||
## Data Layer Structure Best Practices
|
||||
|
||||
### Use Nested Objects for Organization
|
||||
|
||||
```javascript
|
||||
dataLayer.push({
|
||||
'event': 'purchase',
|
||||
'transaction': {
|
||||
'id': 'T12345',
|
||||
'revenue': 99.99,
|
||||
'tax': 9.99,
|
||||
'shipping': 5.00
|
||||
},
|
||||
'customer': {
|
||||
'id': 'C67890',
|
||||
'type': 'returning',
|
||||
'lifetime_value': 1500.00
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Use Arrays for Multiple Similar Items
|
||||
|
||||
```javascript
|
||||
dataLayer.push({
|
||||
'event': 'product_impressions',
|
||||
'products': [
|
||||
{'id': 'P1', 'name': 'Blue Shoes', 'price': 49.99},
|
||||
{'id': 'P2', 'name': 'Red Shoes', 'price': 59.99},
|
||||
{'id': 'P3', 'name': 'Green Shoes', 'price': 54.99}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### Use Clear, Descriptive Names
|
||||
|
||||
**Bad:**
|
||||
|
||||
```javascript
|
||||
dataLayer.push({
|
||||
'e': 'clk',
|
||||
'v': '100',
|
||||
't': 'btn'
|
||||
});
|
||||
```
|
||||
|
||||
**Good:**
|
||||
|
||||
```javascript
|
||||
dataLayer.push({
|
||||
'event': 'button_click',
|
||||
'value': '100',
|
||||
'element_type': 'button'
|
||||
});
|
||||
```
|
||||
|
||||
## Testing and Validation
|
||||
|
||||
### 1. Use Debug Mode
|
||||
|
||||
Enable debug mode in Tag Manager to verify data layer pushes in real-time.
|
||||
|
||||
### 2. Console Logging
|
||||
|
||||
Check data layer contents in the browser console:
|
||||
|
||||
```javascript
|
||||
console.log(window.dataLayer);
|
||||
```
|
||||
|
||||
### 3. Data Layer Checker Extensions
|
||||
|
||||
Use browser extensions like:
|
||||
|
||||
- Google Tag Assistant
|
||||
- dataLayer Checker
|
||||
- Tag Manager/Analytics Debugger
|
||||
|
||||
### 4. Preview Mode
|
||||
|
||||
Always test changes in Tag Manager Preview mode before publishing.
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### 1. Push Data Before It's Needed
|
||||
|
||||
Push data layer variables before the tags that need them fire.
|
||||
|
||||
### 2. Avoid Excessive Pushes
|
||||
|
||||
Consolidate data into single pushes when possible:
|
||||
|
||||
**Bad:**
|
||||
|
||||
```javascript
|
||||
dataLayer.push({'userId': '123'});
|
||||
dataLayer.push({'userType': 'premium'});
|
||||
dataLayer.push({'event': 'user_data'});
|
||||
```
|
||||
|
||||
**Good:**
|
||||
|
||||
```javascript
|
||||
dataLayer.push({
|
||||
'userId': '123',
|
||||
'userType': 'premium',
|
||||
'event': 'user_data'
|
||||
});
|
||||
```
|
||||
|
||||
### 3. Don't Push Unnecessarily Large Data
|
||||
|
||||
Avoid pushing very large arrays or objects that won't be used.
|
||||
|
||||
### 4. Clear Data for SPAs
|
||||
|
||||
For single-page applications, reset the data layer between virtual page views:
|
||||
|
||||
```javascript
|
||||
window.dataLayer.push(function() {
|
||||
this.reset();
|
||||
});
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
### Maintain a Data Layer Specification
|
||||
|
||||
Document all data layer variables:
|
||||
|
||||
- Variable name
|
||||
- Data type
|
||||
- When it's populated
|
||||
- Example values
|
||||
- Pages where it appears
|
||||
- Tags/triggers that use it
|
||||
|
||||
### Example Documentation Format
|
||||
|
||||
```markdown
|
||||
## userType
|
||||
|
||||
- **Type:** String
|
||||
- **Values:** 'guest', 'registered', 'premium'
|
||||
- **Populated:** On all pages after user identification
|
||||
- **Example:** `{'userType': 'premium'}`
|
||||
- **Used by:** User Segmentation Tags, Personalization Triggers
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- [Google Tag Manager Data Layer Documentation](https://developers.google.com/tag-platform/tag-manager/datalayer)
|
||||
- [Data Layer Best Practices (Google Support)](https://support.google.com/tagmanager/answer/6164391)
|
||||
- [GTM Help Center](https://support.google.com/tagmanager)
|
||||
258
skills/gtm-datalayer/references/datalayer-fundamentals.md
Normal file
258
skills/gtm-datalayer/references/datalayer-fundamentals.md
Normal file
@@ -0,0 +1,258 @@
|
||||
# The Data Layer
|
||||
|
||||
**Source**: https://developers.google.com/tag-platform/tag-manager/datalayer
|
||||
**Extracted**: 2025-01-09
|
||||
|
||||
The data layer is an object used by Google Tag Manager and gtag.js to pass information to tags. Events or variables can be passed via the data layer, and triggers can be set up based on the values of variables.
|
||||
|
||||
For example, if you fire a remarketing tag when the value of `purchase_total` is greater than $100, or based on the specific events, e.g. when a button is clicked, your data layer can be configured to make that data available to your tags. The data layer object is structured as JSON. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "checkout_button",
|
||||
"gtm": {
|
||||
"uniqueEventId": 2,
|
||||
"start": 1639524976560,
|
||||
"scrollThreshold": 90,
|
||||
"scrollUnits": "percent",
|
||||
"scrollDirection": "vertical",
|
||||
"triggers": "1_27"
|
||||
},
|
||||
"value": "120"
|
||||
}
|
||||
```
|
||||
|
||||
Google tags are designed to easily reference information that is added to the data layer in an organized and predictable way, rather than by parsing variables, transaction information, page categories, and other signals scattered throughout your page. A data layer implementation populated with variables and associated values will help to ensure that relevant data is available when your tags need them.
|
||||
|
||||
## Installation
|
||||
|
||||
For Tag Manager web page installations, you must create a data layer. The data layer is established before Tag Manager is loaded:
|
||||
|
||||
```javascript
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
</script>
|
||||
<!-- Google Tag Manager -->
|
||||
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
||||
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
||||
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
||||
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
||||
})(window,document,'script','dataLayer','GTM-XXXXXX');</script>
|
||||
<!-- End Google Tag Manager -->
|
||||
```
|
||||
|
||||
In standard gtag.js implementations where the tag has been copied from within the product and added to a web page, the code to establish the data layer is provided. In custom implementations of the Google tag, add the data layer code at the beginning of your script:
|
||||
|
||||
```javascript
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=TAG_ID"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments)};
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', 'TAG_ID');
|
||||
</script>
|
||||
```
|
||||
|
||||
## How Data Layer Information is Processed
|
||||
|
||||
When a container is loaded, Tag Manager will begin to process all queued data layer push messages. Tag Manager processes messages on a first-in, first-out basis: Each message is processed one at a time, in the order it was received. If the message is an event, any tags with trigger conditions that have been met will fire before Tag Manager moves on to the next message.
|
||||
|
||||
If a `gtag()` or `dataLayer.push()` call is made by code on a page, in a Custom Template, or in a Custom HTML tag, the associated message is queued and processed after all other pending messages are evaluated. This means that any updated data layer values are not guaranteed to be available for the next event. To handle these cases, you should add an event name to a message as it is pushed to the data layer, and then listen for that event name with a Custom Event trigger.
|
||||
|
||||
## Use a Data Layer with Event Handlers
|
||||
|
||||
The `dataLayer` object uses an `event` command to initiate the sending of events.
|
||||
|
||||
The Google tag and Tag Manager use a special data layer variable called `event` that is used by JavaScript event listeners to fire tags when a user interacts with website elements. For example, you may want to fire a conversion tracking tag when a user clicks a purchase confirmation button. Events may be called whenever a user interacts with website elements such as links, buttons, scrolls, etc.
|
||||
|
||||
This functionality is accomplished by calling `dataLayer.push()` when an event occurs. The syntax for sending an event with `dataLayer.push()` is as follows:
|
||||
|
||||
```javascript
|
||||
dataLayer.push({'event': 'event_name'});
|
||||
```
|
||||
|
||||
Where `event_name` is a string that describes the event, such as `'login'`, `purchase`, or `search`.
|
||||
|
||||
Use `dataLayer.push()` to send event data when an action occurs that you'd like to measure. For example, to send an event when a user clicks a button, modify the button's `onclick` handler:
|
||||
|
||||
```html
|
||||
<button onclick="dataLayer.push({'event': 'login'});">Button 1</button>
|
||||
```
|
||||
|
||||
You can push data layer variables to the data layer dynamically to capture information such as values entered or selected in a form, metadata associated with a video that the visitor is playing, the color of a product (e.g. a car) customized by the visitor, the destination URLs of clicked links, etc.
|
||||
|
||||
The basic syntax for setting dynamic data layer variables is as follows:
|
||||
|
||||
```javascript
|
||||
dataLayer.push({'variable_name': 'variable_value'});
|
||||
```
|
||||
|
||||
Where `'variable_name'` is a string indicating the name of the data layer variable to be set, and `'variable_value'` is a string indicating the value of the data layer variable to be set or replaced.
|
||||
|
||||
For example, to set a data layer variable with a color preference when a visitor engages with a product customization tool:
|
||||
|
||||
```javascript
|
||||
dataLayer.push({'color': 'red'});
|
||||
```
|
||||
|
||||
## One Push, Multiple Variables
|
||||
|
||||
You can push multiple variables and events at once:
|
||||
|
||||
```javascript
|
||||
dataLayer.push({
|
||||
'color': 'red',
|
||||
'conversionValue': 50,
|
||||
'event': 'customize'
|
||||
});
|
||||
```
|
||||
|
||||
## Persist Data Layer Variables
|
||||
|
||||
To persist data layer variables between web pages, call `dataLayer.push()` after the data layer has been instantiated on each page load, and push the variables to the data layer. If you want these data layer variables to be available to Tag Manager when the container is loaded, add a `dataLayer.push()` call above the Tag Manager container code:
|
||||
|
||||
```javascript
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
|
||||
dataLayer.push({
|
||||
'event': 'Pageview',
|
||||
'pagePath': 'https://www.googleanalytics.dev/pancakes',
|
||||
'pageTitle': 'Pancake Event Signup',
|
||||
'visitorType': 'customer'
|
||||
});
|
||||
</script>
|
||||
<!-- Google Tag Manager -->
|
||||
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
||||
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
||||
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
||||
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
||||
})(window,document,'script','dataLayer','GTM-XXXXXX');</script>
|
||||
<!-- End Google Tag Manager -->
|
||||
```
|
||||
|
||||
Each variable declared within the data layer object will persist only as long as the visitor remains on the current page. Data layer variables that are relevant across pages (e.g. `visitorType`) must be declared in the data layer on each page of your website. While you don't need to put the same set of variables in the data layer on every page, you should use a consistent naming convention. In other words: if you set the page category on the signup page using a variable called `pageCategory`, your product and purchase pages should use the `pageCategory` variable as well.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Here are some data layer troubleshooting tips:
|
||||
|
||||
**Do not overwrite the `window.dataLayer` variable:** When you use the data layer directly (e.g. `dataLayer = [{'item': 'value'}]`), it will overwrite any existing values in the `dataLayer`. Tag Manager installations should instantiate the data layer as high up in the source code as possible, above the container snippet, using `window.dataLayer = window.dataLayer || [];`. After the `dataLayer` has been declared, use `dataLayer.push({'item': 'value'})` to add additional values to it, and if those values need to be available to Tag Manager when the page loads, then that `dataLayer.push()` call needs to be above the Tag Manager container code as well.
|
||||
|
||||
**The `dataLayer` object name is case-sensitive:** If you try to push a variable or event without the proper casing, the push will not work.
|
||||
|
||||
```javascript
|
||||
datalayer.push({'pageTitle': 'Home'}); // Bad (datalayer in lowercase)
|
||||
dataLayer.push({'pageTitle': 'Home'}); // Good (dataLayer in camel case)
|
||||
```
|
||||
|
||||
`dataLayer.push` must be called with valid JavaScript objects. All data layer variable names should be enclosed in quotes.
|
||||
|
||||
```javascript
|
||||
dataLayer.push({new-variable: 'value'}); // Bad - no quote marks
|
||||
dataLayer.push({'new-variable': 'value'}); // Good - proper quote marks
|
||||
```
|
||||
|
||||
**Keep variable names consistent across pages:** If you use different variable names for the same concept on different pages, your tags will be unable to consistently fire in all desired locations.
|
||||
|
||||
Bad:
|
||||
```javascript
|
||||
// Homepage:
|
||||
dataLayer.push({'visitorType': 'low-value'});
|
||||
|
||||
// Checkout Page:
|
||||
dataLayer.push({'visitor_type': 'high-value'});
|
||||
```
|
||||
|
||||
Good:
|
||||
```javascript
|
||||
// Homepage:
|
||||
dataLayer.push({'visitorType': 'low-value'});
|
||||
|
||||
// Checkout Page:
|
||||
dataLayer.push({'visitorType': 'high-value'});
|
||||
```
|
||||
|
||||
## Rename the Data Layer
|
||||
|
||||
The default name of the data layer object initiated by the Google tag or Tag Manager is `dataLayer`. If you'd prefer to use a different name for your data layer, you may do so by editing the data layer parameter value in your Google tag or Tag Manager container snippet with the name of your choice.
|
||||
|
||||
### For gtag.js
|
||||
|
||||
Add a query parameter named "l" to the URL to set the new data layer name, e.g. `l=myNewName`. Update all instances of `dataLayer` in the Google tag snippet to the new name:
|
||||
|
||||
```javascript
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=TAG_ID&l=myNewName"></script>
|
||||
<script>
|
||||
window.myNewName = window.myNewName || [];
|
||||
function gtag(){myNewName.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', 'TAG_ID');
|
||||
</script>
|
||||
```
|
||||
|
||||
### For Tag Manager
|
||||
|
||||
Replace the data layer parameter value in your container snippet with the name of your choice:
|
||||
|
||||
```javascript
|
||||
<!-- Google Tag Manager -->
|
||||
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
||||
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
||||
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
||||
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
||||
})(window,document,'script','myNewName','GTM-XXXXXX');</script>
|
||||
<!-- End Google Tag Manager -->
|
||||
```
|
||||
|
||||
Once renamed, all references to your data layer (i.e. when declaring the data layer above the snippet, or when pushing events or dynamic data layer variables to the data layer with the `.push()` command) must be adjusted to reflect your custom data layer name:
|
||||
|
||||
```javascript
|
||||
<script>
|
||||
myNewName = window.dataLayer || [];
|
||||
myNewName.push({'variable_name': 'variable_value'});
|
||||
</script>
|
||||
```
|
||||
|
||||
## Custom Data Layer Methods
|
||||
|
||||
If you push a function to the data layer, it will be invoked with this set to an abstract data model. This abstract data model can get and set values to a key value store, and also provides a way to reset the data layer.
|
||||
|
||||
### Set
|
||||
|
||||
The `set` function on the abstract data model lets you set values to retrieve through get:
|
||||
|
||||
```javascript
|
||||
window.dataLayer.push(function() {
|
||||
this.set('time', new Date());
|
||||
});
|
||||
```
|
||||
|
||||
### Get
|
||||
|
||||
The `get` function on the abstract data model lets you retrieve values that were set:
|
||||
|
||||
```javascript
|
||||
window.dataLayer.push(function() {
|
||||
const existingTime = this.get('time');
|
||||
if (existingTime !== null) {
|
||||
// Change behavior based on whether or not this value exists...
|
||||
} else {
|
||||
// ...
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Reset
|
||||
|
||||
The `reset` function on the abstract data model lets you reset the data in the data layer. This is most useful with a page that will remain open and the data layer size continues to grow over time:
|
||||
|
||||
```javascript
|
||||
window.dataLayer.push(function() {
|
||||
this.reset();
|
||||
})
|
||||
```
|
||||
542
skills/gtm-datalayer/references/ecommerce-datalayer.md
Normal file
542
skills/gtm-datalayer/references/ecommerce-datalayer.md
Normal file
@@ -0,0 +1,542 @@
|
||||
# E-commerce Data Layer Patterns
|
||||
|
||||
## Overview
|
||||
|
||||
Ecommerce events in Google Analytics 4 (GA4) enable you to collect information about shopping behavior and quantify your most popular products, measure the influence of promotions and product placement on revenue.
|
||||
|
||||
## E-commerce Object Structure
|
||||
|
||||
### GA4 E-commerce Schema
|
||||
|
||||
All ecommerce events use an `items` array to represent products and services. The items array can include up to 200 elements and supports up to 27 custom parameters in addition to prescribed parameters.
|
||||
|
||||
#### Item Object Structure
|
||||
|
||||
```javascript
|
||||
{
|
||||
item_id: "SKU_12345", // Required
|
||||
item_name: "Stan and Friends Tee", // Required
|
||||
affiliation: "Google Merchandise Store",
|
||||
coupon: "SUMMER_FUN",
|
||||
discount: 2.22,
|
||||
index: 0,
|
||||
item_brand: "Google",
|
||||
item_category: "Apparel",
|
||||
item_category2: "Adult",
|
||||
item_category3: "Shirts",
|
||||
item_category4: "Crew",
|
||||
item_category5: "Short sleeve",
|
||||
item_list_id: "related_products",
|
||||
item_list_name: "Related Products",
|
||||
item_variant: "green",
|
||||
location_id: "ChIJIQBpAG2ahYAR_6128GcTUEo",
|
||||
price: 10.01,
|
||||
quantity: 3
|
||||
}
|
||||
```
|
||||
|
||||
## E-commerce Events
|
||||
|
||||
### 1. View Item List
|
||||
|
||||
When a user is presented with a list of results (e.g., search results, category page), send a `view_item_list` event:
|
||||
|
||||
```javascript
|
||||
gtag("event", "view_item_list", {
|
||||
item_list_id: "related_products",
|
||||
item_list_name: "Related products",
|
||||
items: [
|
||||
{
|
||||
item_id: "SKU_12345",
|
||||
item_name: "Stan and Friends Tee",
|
||||
affiliation: "Google Merchandise Store",
|
||||
coupon: "SUMMER_FUN",
|
||||
discount: 2.22,
|
||||
index: 0,
|
||||
item_brand: "Google",
|
||||
item_category: "Apparel",
|
||||
item_category2: "Adult",
|
||||
item_category3: "Shirts",
|
||||
item_category4: "Crew",
|
||||
item_category5: "Short sleeve",
|
||||
item_list_id: "related_products",
|
||||
item_list_name: "Related Products",
|
||||
item_variant: "green",
|
||||
location_id: "ChIJIQBpAG2ahYAR_6128GcTUEo",
|
||||
price: 10.01,
|
||||
quantity: 3
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### 2. Select Item
|
||||
|
||||
When a user selects an item from the list:
|
||||
|
||||
```javascript
|
||||
gtag("event", "select_item", {
|
||||
item_list_id: "related_products",
|
||||
item_list_name: "Related products",
|
||||
items: [
|
||||
{
|
||||
item_id: "SKU_12345",
|
||||
item_name: "Stan and Friends Tee",
|
||||
// ... other item parameters
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### 3. View Item Details
|
||||
|
||||
Measure how many times item details are viewed:
|
||||
|
||||
```javascript
|
||||
gtag("event", "view_item", {
|
||||
currency: "USD",
|
||||
value: 30.03,
|
||||
items: [
|
||||
{
|
||||
item_id: "SKU_12345",
|
||||
item_name: "Stan and Friends Tee",
|
||||
price: 10.01,
|
||||
quantity: 3
|
||||
// ... other item parameters
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### 4. Add to Cart
|
||||
|
||||
Measure when an item is added to a shopping cart:
|
||||
|
||||
```javascript
|
||||
gtag("event", "add_to_cart", {
|
||||
currency: "USD",
|
||||
value: 30.03,
|
||||
items: [
|
||||
{
|
||||
item_id: "SKU_12345",
|
||||
item_name: "Stan and Friends Tee",
|
||||
price: 10.01,
|
||||
quantity: 3
|
||||
// ... other item parameters
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### 5. Remove from Cart
|
||||
|
||||
Measure when a user removes an item from cart:
|
||||
|
||||
```javascript
|
||||
gtag("event", "remove_from_cart", {
|
||||
currency: "USD",
|
||||
value: 30.03,
|
||||
items: [
|
||||
{
|
||||
item_id: "SKU_12345",
|
||||
item_name: "Stan and Friends Tee",
|
||||
price: 10.01,
|
||||
quantity: 3
|
||||
// ... other item parameters
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### 6. View Cart
|
||||
|
||||
When a user views the cart:
|
||||
|
||||
```javascript
|
||||
gtag("event", "view_cart", {
|
||||
currency: "USD",
|
||||
value: 30.03,
|
||||
items: [
|
||||
{
|
||||
item_id: "SKU_12345",
|
||||
item_name: "Stan and Friends Tee",
|
||||
price: 10.01,
|
||||
quantity: 3
|
||||
// ... other item parameters
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### 7. Add to Wishlist
|
||||
|
||||
Measure when an item is added to a wishlist:
|
||||
|
||||
```javascript
|
||||
gtag("event", "add_to_wishlist", {
|
||||
currency: "USD",
|
||||
value: 30.03,
|
||||
items: [
|
||||
{
|
||||
item_id: "SKU_12345",
|
||||
item_name: "Stan and Friends Tee",
|
||||
price: 10.01,
|
||||
quantity: 3
|
||||
// ... other item parameters
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### 8. Begin Checkout
|
||||
|
||||
Measure the first step in a checkout process:
|
||||
|
||||
```javascript
|
||||
gtag("event", "begin_checkout", {
|
||||
currency: "USD",
|
||||
value: 30.03,
|
||||
coupon: "SUMMER_FUN",
|
||||
items: [
|
||||
{
|
||||
item_id: "SKU_12345",
|
||||
item_name: "Stan and Friends Tee",
|
||||
price: 10.01,
|
||||
quantity: 3
|
||||
// ... other item parameters
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### 9. Add Shipping Info
|
||||
|
||||
When a user adds shipping information:
|
||||
|
||||
```javascript
|
||||
gtag("event", "add_shipping_info", {
|
||||
currency: "USD",
|
||||
value: 30.03,
|
||||
coupon: "SUMMER_FUN",
|
||||
shipping_tier: "Ground",
|
||||
items: [
|
||||
{
|
||||
item_id: "SKU_12345",
|
||||
item_name: "Stan and Friends Tee",
|
||||
price: 10.01,
|
||||
quantity: 3
|
||||
// ... other item parameters
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### 10. Add Payment Info
|
||||
|
||||
When a user submits payment information:
|
||||
|
||||
```javascript
|
||||
gtag("event", "add_payment_info", {
|
||||
currency: "USD",
|
||||
value: 30.03,
|
||||
coupon: "SUMMER_FUN",
|
||||
payment_type: "Credit Card",
|
||||
items: [
|
||||
{
|
||||
item_id: "SKU_12345",
|
||||
item_name: "Stan and Friends Tee",
|
||||
price: 10.01,
|
||||
quantity: 3
|
||||
// ... other item parameters
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### 11. Purchase
|
||||
|
||||
Measure a purchase:
|
||||
|
||||
```javascript
|
||||
gtag("event", "purchase", {
|
||||
transaction_id: "T_12345", // Required
|
||||
value: 72.05, // Sum of (price * quantity) for all items
|
||||
tax: 3.60,
|
||||
shipping: 5.99,
|
||||
currency: "USD",
|
||||
coupon: "SUMMER_SALE",
|
||||
customer_type: "new",
|
||||
items: [
|
||||
{
|
||||
item_id: "SKU_12345",
|
||||
item_name: "Stan and Friends Tee",
|
||||
affiliation: "Google Merchandise Store",
|
||||
coupon: "SUMMER_FUN",
|
||||
discount: 2.22,
|
||||
index: 0,
|
||||
item_brand: "Google",
|
||||
item_category: "Apparel",
|
||||
item_category2: "Adult",
|
||||
item_category3: "Shirts",
|
||||
item_category4: "Crew",
|
||||
item_category5: "Short sleeve",
|
||||
item_list_id: "related_products",
|
||||
item_list_name: "Related Products",
|
||||
item_variant: "green",
|
||||
location_id: "ChIJIQBpAG2ahYAR_6128GcTUEo",
|
||||
price: 10.01,
|
||||
quantity: 3
|
||||
},
|
||||
{
|
||||
item_id: "SKU_12346",
|
||||
item_name: "Google Grey Women's Tee",
|
||||
affiliation: "Google Merchandise Store",
|
||||
coupon: "SUMMER_FUN",
|
||||
discount: 3.33,
|
||||
index: 1,
|
||||
item_brand: "Google",
|
||||
item_category: "Apparel",
|
||||
price: 21.01,
|
||||
promotion_id: "P_12345",
|
||||
promotion_name: "Summer Sale",
|
||||
quantity: 2
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### 12. Refund
|
||||
|
||||
Measure refunds with the relevant transaction ID:
|
||||
|
||||
```javascript
|
||||
gtag("event", "refund", {
|
||||
currency: "USD",
|
||||
transaction_id: "T_12345", // Required
|
||||
value: 30.03,
|
||||
coupon: "SUMMER_FUN",
|
||||
shipping: 3.33,
|
||||
tax: 1.11,
|
||||
items: [
|
||||
{
|
||||
item_id: "SKU_12345",
|
||||
item_name: "Stan and Friends Tee",
|
||||
price: 10.01,
|
||||
quantity: 3
|
||||
// ... other item parameters
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
## Promotions
|
||||
|
||||
### View Promotion
|
||||
|
||||
Measure promotion impressions:
|
||||
|
||||
```javascript
|
||||
gtag("event", "view_promotion", {
|
||||
creative_name: "Summer Banner",
|
||||
creative_slot: "featured_app_1",
|
||||
promotion_id: "P_12345",
|
||||
promotion_name: "Summer Sale",
|
||||
items: [
|
||||
{
|
||||
item_id: "SKU_12345",
|
||||
item_name: "Stan and Friends Tee",
|
||||
// ... other item parameters
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### Select Promotion
|
||||
|
||||
Indicate a user clicked on a promotion:
|
||||
|
||||
```javascript
|
||||
gtag("event", "select_promotion", {
|
||||
creative_name: "Summer Banner",
|
||||
creative_slot: "featured_app_1",
|
||||
promotion_id: "P_12345",
|
||||
promotion_name: "Summer Sale",
|
||||
items: [
|
||||
{
|
||||
item_id: "SKU_12345",
|
||||
item_name: "Stan and Friends Tee",
|
||||
// ... other item parameters
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Currency Formatting
|
||||
|
||||
- Always set `currency` when sending `value` (revenue) data
|
||||
- Ensures revenue metrics are calculated correctly
|
||||
- Use standard 3-letter ISO 4217 currency codes (e.g., "USD", "EUR", "GBP")
|
||||
|
||||
### 2. Product IDs
|
||||
|
||||
- Use consistent product IDs across all events
|
||||
- Ensure `item_id` matches your product catalog
|
||||
- Include both `item_id` and `item_name` for better reporting
|
||||
|
||||
### 3. Item Array Structure
|
||||
|
||||
- Populate all available ecommerce parameters you have data for
|
||||
- Even if a parameter is optional, include it when you have the data
|
||||
- Helps maximize the utility of your ecommerce reporting
|
||||
|
||||
### 4. Event Timing
|
||||
|
||||
- Fire events at the appropriate time in the user journey
|
||||
- Don't fire `purchase` events until transaction is confirmed
|
||||
- Fire `view_item_list` when the list initially loads, not on interaction
|
||||
|
||||
### 5. Data Quality Validation
|
||||
|
||||
- Use [debug mode](https://support.google.com/analytics/answer/7201382) to verify events in realtime
|
||||
- Check that parameters match expected values
|
||||
- Verify currency and value calculations are correct
|
||||
- Test the complete funnel from browsing to purchase
|
||||
|
||||
### 6. Custom Dimensions and Metrics
|
||||
|
||||
- Review [custom dimension and metric limits](https://support.google.com/analytics/answer/10075209#limits)
|
||||
- Plan your custom parameters within these limits
|
||||
- Document your custom parameter usage
|
||||
|
||||
### 7. Sample E-commerce Website
|
||||
|
||||
- Use [the Google sample ecommerce website](https://enhancedecommerce.appspot.com/) as a reference
|
||||
- See practical examples of proper tagging
|
||||
- Test your implementation against working examples
|
||||
|
||||
## Implementation Tips
|
||||
|
||||
### Setting Currency at Event Level
|
||||
|
||||
When setting the items array outside of the ecommerce object, set the `currency` parameter at the event level:
|
||||
|
||||
```javascript
|
||||
gtag("event", "purchase", {
|
||||
transaction_id: "T_12345",
|
||||
currency: "USD", // Event-level currency
|
||||
value: 72.05,
|
||||
items: [
|
||||
// items array
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### Consistent Naming Conventions
|
||||
|
||||
Maintain consistent naming across your implementation:
|
||||
|
||||
- Use snake_case for event names (following GA4 conventions)
|
||||
- Keep parameter names consistent with GA4 recommended events
|
||||
- Document any custom parameters you add
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Product Listing Pages
|
||||
|
||||
```javascript
|
||||
// On page load
|
||||
gtag("event", "view_item_list", {
|
||||
item_list_id: "category_shirts",
|
||||
item_list_name: "Shirts",
|
||||
items: [/* all visible products */]
|
||||
});
|
||||
|
||||
// On product click
|
||||
gtag("event", "select_item", {
|
||||
item_list_id: "category_shirts",
|
||||
item_list_name: "Shirts",
|
||||
items: [/* clicked product */]
|
||||
});
|
||||
```
|
||||
|
||||
### Product Detail Pages
|
||||
|
||||
```javascript
|
||||
// On page load
|
||||
gtag("event", "view_item", {
|
||||
currency: "USD",
|
||||
value: 29.99,
|
||||
items: [/* product details */]
|
||||
});
|
||||
|
||||
// On add to cart button click
|
||||
gtag("event", "add_to_cart", {
|
||||
currency: "USD",
|
||||
value: 29.99,
|
||||
items: [/* product with quantity */]
|
||||
});
|
||||
```
|
||||
|
||||
### Shopping Cart
|
||||
|
||||
```javascript
|
||||
// When viewing cart
|
||||
gtag("event", "view_cart", {
|
||||
currency: "USD",
|
||||
value: 150.00,
|
||||
items: [/* all cart items */]
|
||||
});
|
||||
|
||||
// When removing item
|
||||
gtag("event", "remove_from_cart", {
|
||||
currency: "USD",
|
||||
value: 29.99,
|
||||
items: [/* removed item */]
|
||||
});
|
||||
```
|
||||
|
||||
### Checkout Flow
|
||||
|
||||
```javascript
|
||||
// Step 1: Begin checkout
|
||||
gtag("event", "begin_checkout", {
|
||||
currency: "USD",
|
||||
value: 150.00,
|
||||
items: [/* all cart items */]
|
||||
});
|
||||
|
||||
// Step 2: Add shipping
|
||||
gtag("event", "add_shipping_info", {
|
||||
currency: "USD",
|
||||
value: 150.00,
|
||||
shipping_tier: "Ground",
|
||||
items: [/* all cart items */]
|
||||
});
|
||||
|
||||
// Step 3: Add payment
|
||||
gtag("event", "add_payment_info", {
|
||||
currency: "USD",
|
||||
value: 150.00,
|
||||
payment_type: "Credit Card",
|
||||
items: [/* all cart items */]
|
||||
});
|
||||
|
||||
// Step 4: Complete purchase
|
||||
gtag("event", "purchase", {
|
||||
transaction_id: "T_12345",
|
||||
value: 155.99,
|
||||
tax: 10.00,
|
||||
shipping: 5.99,
|
||||
currency: "USD",
|
||||
items: [/* all purchased items */]
|
||||
});
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- [GA4 E-commerce Documentation](https://developers.google.com/analytics/devguides/collection/ga4/ecommerce)
|
||||
- [GA4 Recommended Events Reference](https://developers.google.com/analytics/devguides/collection/ga4/reference/events)
|
||||
- [Set up a Purchase Event Guide](https://developers.google.com/analytics/devguides/collection/ga4/set-up-ecommerce)
|
||||
- [Sample E-commerce Website](https://enhancedecommerce.appspot.com/)
|
||||
472
skills/gtm-datalayer/references/spa-datalayer.md
Normal file
472
skills/gtm-datalayer/references/spa-datalayer.md
Normal file
@@ -0,0 +1,472 @@
|
||||
# Data Layer for Single-Page Applications (SPAs)
|
||||
|
||||
**Source**: https://developers.google.com/analytics/devguides/collection/ga4/single-page-applications
|
||||
**Last Updated**: 2025-01-09
|
||||
|
||||
## Overview
|
||||
|
||||
Single-Page Applications (SPAs) are websites that load an HTML document once and fetch additional content using JavaScript APIs. Unlike traditional multi-page applications, SPAs require special handling for tracking page views and user interactions because the browser doesn't reload the page when users navigate between different screens.
|
||||
|
||||
The key challenge with SPAs is that traditional page view tracking relies on full page loads, but in SPAs, navigation happens dynamically without triggering a new page load event.
|
||||
|
||||
## Why SPAs Require Special Treatment
|
||||
|
||||
### Traditional vs SPA Page Loads
|
||||
|
||||
**Traditional Website:**
|
||||
- User clicks a link
|
||||
- Browser requests a new HTML document
|
||||
- Page fully reloads
|
||||
- Analytics tags fire automatically on load
|
||||
|
||||
**Single-Page Application:**
|
||||
- User clicks a link
|
||||
- JavaScript updates the DOM
|
||||
- URL may change (via History API)
|
||||
- No page reload occurs
|
||||
- Analytics tags don't automatically fire
|
||||
|
||||
### Key Measurement Goals
|
||||
|
||||
To accurately track SPAs, you need to:
|
||||
|
||||
1. **Count page views for each screen** a user interacts with
|
||||
2. **Track the page referrer correctly** to trace the user journey
|
||||
3. **Maintain proper event sequencing** as users navigate
|
||||
4. **Clear previous page data** to avoid data carryover
|
||||
5. **Update page-specific parameters** for each virtual page view
|
||||
|
||||
## Implementation Methods
|
||||
|
||||
### Method 1: Browser History Changes (Recommended)
|
||||
|
||||
Use this method if your SPA uses the [History API](https://developer.mozilla.org/en-US/docs/Web/API/History), specifically the `pushState()` and `replaceState()` methods to update screens.
|
||||
|
||||
#### How It Works
|
||||
|
||||
GTM's **History Change trigger** listens for:
|
||||
- Changes to the URL fragment (hash)
|
||||
- Calls to `history.pushState()`
|
||||
- Calls to `history.replaceState()`
|
||||
- Browser back/forward button clicks (`popstate` event)
|
||||
|
||||
#### GTM Setup
|
||||
|
||||
1. **Enable Built-in History Variables** in GTM:
|
||||
- History Old URL Fragment
|
||||
- History New URL Fragment
|
||||
- History Old State
|
||||
- History New State
|
||||
- History Source (pushState, replaceState, popstate, or hashchange)
|
||||
|
||||
2. **Create a History Change Trigger:**
|
||||
- Go to Triggers > New
|
||||
- Choose "History Change" as trigger type
|
||||
- Configure any additional filters if needed
|
||||
- This trigger fires whenever the URL changes without a page reload
|
||||
|
||||
3. **Create a Virtual Page View Tag:**
|
||||
- Create a GA4 Event tag
|
||||
- Event Name: `page_view`
|
||||
- Set to fire on the History Change trigger
|
||||
- Configure page parameters (page_location, page_title, etc.)
|
||||
|
||||
#### Example Implementation
|
||||
|
||||
```javascript
|
||||
// Your SPA framework (e.g., React Router) handles navigation
|
||||
// It uses pushState internally:
|
||||
history.pushState({ page: 'products' }, 'Products', '/products');
|
||||
|
||||
// GTM's History Change trigger automatically detects this
|
||||
// and fires your configured tags
|
||||
```
|
||||
|
||||
#### Data Layer Push Pattern
|
||||
|
||||
When using History Change triggers, push updated page data to the data layer:
|
||||
|
||||
```javascript
|
||||
// Clear previous page data and push new page data
|
||||
window.dataLayer.push({
|
||||
event: 'virtual_pageview',
|
||||
page_path: '/new-page',
|
||||
page_title: 'New Page Title',
|
||||
page_location: window.location.href,
|
||||
// Clear previous page-scoped variables
|
||||
previous_page_data: undefined,
|
||||
transaction_id: undefined // Clear transaction-specific data
|
||||
});
|
||||
```
|
||||
|
||||
### Method 2: Custom Events
|
||||
|
||||
Use this method if your website uses the [`DocumentFragment`](https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment) object to render different screens, or if you need more control over when virtual page views fire.
|
||||
|
||||
#### How It Works
|
||||
|
||||
You manually push custom events to the data layer when screen changes occur, rather than relying on automatic History API detection.
|
||||
|
||||
#### Implementation
|
||||
|
||||
```javascript
|
||||
// In your SPA's route change handler
|
||||
function onRouteChange(newRoute) {
|
||||
// Update the DOM with new content
|
||||
updateContent(newRoute);
|
||||
|
||||
// Push virtual pageview to data layer
|
||||
window.dataLayer.push({
|
||||
event: 'virtual_pageview',
|
||||
page_path: newRoute.path,
|
||||
page_title: newRoute.title,
|
||||
page_location: window.location.href
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
#### GTM Setup
|
||||
|
||||
1. **Create a Custom Event Trigger:**
|
||||
- Go to Triggers > New
|
||||
- Choose "Custom Event" as trigger type
|
||||
- Event name: `virtual_pageview`
|
||||
|
||||
2. **Create Variables for Page Data:**
|
||||
- Data Layer Variable for `page_path`
|
||||
- Data Layer Variable for `page_title`
|
||||
- Data Layer Variable for `page_location`
|
||||
|
||||
3. **Create a GA4 Event Tag:**
|
||||
- Event Name: `page_view`
|
||||
- Add event parameters using the variables created above
|
||||
- Set to fire on the Custom Event trigger
|
||||
|
||||
## Data Layer Clearing Best Practices
|
||||
|
||||
### Why Clear Data
|
||||
|
||||
In SPAs, data persists in the data layer until explicitly cleared. This can cause:
|
||||
- Transaction data appearing on non-transaction pages
|
||||
- User data from one session bleeding into another
|
||||
- Incorrect attribution of events to pages
|
||||
|
||||
### What to Clear
|
||||
|
||||
**Page-Scoped Data** (clear on every virtual pageview):
|
||||
- `page_title`
|
||||
- `page_path`
|
||||
- `page_category`
|
||||
- Custom page dimensions
|
||||
|
||||
**Event-Scoped Data** (clear after the event fires):
|
||||
- `transaction_id`
|
||||
- `ecommerce` objects
|
||||
- Form submission data
|
||||
- Click data
|
||||
|
||||
**Persistent Data** (keep across page views):
|
||||
- `user_id`
|
||||
- `user_type`
|
||||
- Session-level dimensions
|
||||
|
||||
### Clearing Pattern
|
||||
|
||||
```javascript
|
||||
// Pattern 1: Clear and set in separate pushes
|
||||
dataLayer.push(function() {
|
||||
this.reset(); // Clear all data layer state
|
||||
});
|
||||
|
||||
dataLayer.push({
|
||||
event: 'virtual_pageview',
|
||||
page_title: 'New Page',
|
||||
page_path: '/new-page'
|
||||
});
|
||||
|
||||
// Pattern 2: Clear and set in one push (set to undefined)
|
||||
dataLayer.push({
|
||||
event: 'virtual_pageview',
|
||||
// Clear previous values
|
||||
transaction_id: undefined,
|
||||
ecommerce: undefined,
|
||||
form_id: undefined,
|
||||
// Set new values
|
||||
page_title: 'New Page',
|
||||
page_path: '/new-page'
|
||||
});
|
||||
```
|
||||
|
||||
## Framework-Specific Implementation
|
||||
|
||||
### React with React Router
|
||||
|
||||
```javascript
|
||||
import { useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
function Analytics() {
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
// Clear previous page data
|
||||
window.dataLayer.push({
|
||||
event: 'virtual_pageview',
|
||||
page_path: location.pathname,
|
||||
page_title: document.title,
|
||||
page_location: window.location.href,
|
||||
// Clear event-scoped data
|
||||
transaction_id: undefined,
|
||||
ecommerce: undefined
|
||||
});
|
||||
}, [location]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export default Analytics;
|
||||
```
|
||||
|
||||
### Vue.js with Vue Router
|
||||
|
||||
```javascript
|
||||
// In your main.js or router configuration
|
||||
import router from './router';
|
||||
|
||||
router.afterEach((to, from) => {
|
||||
window.dataLayer.push({
|
||||
event: 'virtual_pageview',
|
||||
page_path: to.path,
|
||||
page_title: to.meta.title || document.title,
|
||||
page_location: window.location.href,
|
||||
// Clear event-scoped data
|
||||
transaction_id: undefined,
|
||||
ecommerce: undefined
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Angular with Angular Router
|
||||
|
||||
```typescript
|
||||
import { Router, NavigationEnd } from '@angular/router';
|
||||
import { filter } from 'rxjs/operators';
|
||||
|
||||
export class AppComponent {
|
||||
constructor(private router: Router) {
|
||||
this.router.events.pipe(
|
||||
filter(event => event instanceof NavigationEnd)
|
||||
).subscribe((event: NavigationEnd) => {
|
||||
window.dataLayer.push({
|
||||
event: 'virtual_pageview',
|
||||
page_path: event.urlAfterRedirects,
|
||||
page_title: document.title,
|
||||
page_location: window.location.href,
|
||||
// Clear event-scoped data
|
||||
transaction_id: undefined,
|
||||
ecommerce: undefined
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Next.js
|
||||
|
||||
```javascript
|
||||
// In pages/_app.js
|
||||
import { useEffect } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
function MyApp({ Component, pageProps }) {
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
const handleRouteChange = (url) => {
|
||||
window.dataLayer.push({
|
||||
event: 'virtual_pageview',
|
||||
page_path: url,
|
||||
page_title: document.title,
|
||||
page_location: window.location.href,
|
||||
// Clear event-scoped data
|
||||
transaction_id: undefined,
|
||||
ecommerce: undefined
|
||||
});
|
||||
};
|
||||
|
||||
router.events.on('routeChangeComplete', handleRouteChange);
|
||||
return () => {
|
||||
router.events.off('routeChangeComplete', handleRouteChange);
|
||||
};
|
||||
}, [router.events]);
|
||||
|
||||
return <Component {...pageProps} />;
|
||||
}
|
||||
|
||||
export default MyApp;
|
||||
```
|
||||
|
||||
## Testing and Debugging SPAs
|
||||
|
||||
### Preview Mode in GTM
|
||||
|
||||
1. **Enable Preview Mode** in your GTM container
|
||||
2. Navigate through your SPA
|
||||
3. Watch for:
|
||||
- History Change events firing
|
||||
- Custom Event triggers activating
|
||||
- Data layer state changes
|
||||
- Tag firing sequence
|
||||
|
||||
### Debug Console Checklist
|
||||
|
||||
For each virtual page view, verify:
|
||||
|
||||
- [ ] `page_view` event fires
|
||||
- [ ] `page_location` updates correctly
|
||||
- [ ] `page_title` reflects the new screen
|
||||
- [ ] Previous page's `page_location` becomes current `page_referrer`
|
||||
- [ ] Event-scoped data is cleared
|
||||
- [ ] User/session data persists
|
||||
|
||||
### DebugView in GA4
|
||||
|
||||
1. **Enable Debug Mode** in GTM tags:
|
||||
```javascript
|
||||
// Add to your data layer push
|
||||
window.dataLayer.push({
|
||||
event: 'virtual_pageview',
|
||||
debug_mode: true,
|
||||
// ... other parameters
|
||||
});
|
||||
```
|
||||
|
||||
2. **Navigate through your SPA**
|
||||
|
||||
3. **Check in GA4 DebugView:**
|
||||
- Each screen change should create a new `page_view` event
|
||||
- Event parameters should update correctly
|
||||
- Event sequence should match user journey
|
||||
|
||||
### Common SPA Pitfalls
|
||||
|
||||
#### 1. Data Carryover
|
||||
|
||||
**Problem:** Transaction data appears on non-checkout pages
|
||||
|
||||
**Solution:**
|
||||
```javascript
|
||||
// Always clear ecommerce data after it's sent
|
||||
dataLayer.push({
|
||||
event: 'purchase',
|
||||
ecommerce: { /* purchase data */ }
|
||||
});
|
||||
|
||||
// On next page view, clear it
|
||||
dataLayer.push({
|
||||
event: 'virtual_pageview',
|
||||
ecommerce: undefined, // Explicitly clear
|
||||
transaction_id: undefined
|
||||
});
|
||||
```
|
||||
|
||||
#### 2. Missing Virtual Pageviews
|
||||
|
||||
**Problem:** Some navigation doesn't trigger page views
|
||||
|
||||
**Solution:** Ensure all route changes push to data layer, including:
|
||||
- Back/forward button navigation
|
||||
- Hash changes
|
||||
- Modal/dialog state changes (if tracking as pages)
|
||||
|
||||
#### 3. Duplicate Page Views
|
||||
|
||||
**Problem:** Both real and virtual page views fire
|
||||
|
||||
**Solution:**
|
||||
```javascript
|
||||
// Block the initial page_view from GA4 Config tag
|
||||
// Use a Pageview trigger with "Fire on DOM Ready" exception
|
||||
// Or configure GA4 tag to not send page_view on initial load
|
||||
```
|
||||
|
||||
#### 4. Incorrect Referrer
|
||||
|
||||
**Problem:** `page_referrer` doesn't reflect previous virtual page
|
||||
|
||||
**Solution:** GTM automatically handles this when using History Change triggers. For custom events, ensure you're not manually setting `page_referrer`.
|
||||
|
||||
## Advanced Patterns
|
||||
|
||||
### Conditional Virtual Pageviews
|
||||
|
||||
Only track certain route changes:
|
||||
|
||||
```javascript
|
||||
function shouldTrackPageview(newRoute) {
|
||||
// Don't track hash-only changes
|
||||
if (newRoute.startsWith('#')) return false;
|
||||
|
||||
// Don't track query parameter-only changes
|
||||
if (isSamePath(currentRoute, newRoute)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (shouldTrackPageview(newRoute)) {
|
||||
dataLayer.push({
|
||||
event: 'virtual_pageview',
|
||||
page_path: newRoute
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Enhanced Virtual Pageview Data
|
||||
|
||||
Include additional context with virtual pageviews:
|
||||
|
||||
```javascript
|
||||
dataLayer.push({
|
||||
event: 'virtual_pageview',
|
||||
page_path: '/products/category/shoes',
|
||||
page_title: 'Shoes - Products',
|
||||
page_location: window.location.href,
|
||||
// Additional context
|
||||
page_category: 'Products',
|
||||
page_subcategory: 'Shoes',
|
||||
content_group: 'Product Listing',
|
||||
user_journey_step: 'Browse',
|
||||
spa_route_type: 'category_page'
|
||||
});
|
||||
```
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
Before deploying your SPA tracking:
|
||||
|
||||
- [ ] History Change trigger configured (if using History API)
|
||||
- [ ] Custom Event trigger configured (if using custom events)
|
||||
- [ ] Data Layer Variables created for page parameters
|
||||
- [ ] GA4 Event tag configured with `page_view` event
|
||||
- [ ] Initial page load tracked correctly
|
||||
- [ ] All navigation types trigger virtual pageviews:
|
||||
- [ ] Forward navigation
|
||||
- [ ] Back button
|
||||
- [ ] Direct URL changes
|
||||
- [ ] Hash changes (if applicable)
|
||||
- [ ] Data clearing implemented for:
|
||||
- [ ] Transaction data
|
||||
- [ ] Event-scoped variables
|
||||
- [ ] Page-scoped variables
|
||||
- [ ] Tested in Preview Mode
|
||||
- [ ] Verified in GA4 DebugView
|
||||
- [ ] Event sequence matches user journey
|
||||
- [ ] Page referrer updates correctly
|
||||
|
||||
## Resources
|
||||
|
||||
- [Google Analytics: Measure Single-Page Applications](https://developers.google.com/analytics/devguides/collection/ga4/single-page-applications)
|
||||
- [GTM History Change Trigger](https://support.google.com/tagmanager/answer/7679322)
|
||||
- [GTM Built-in Variables for Web](https://support.google.com/tagmanager/answer/7182738)
|
||||
- [History API Documentation (MDN)](https://developer.mozilla.org/en-US/docs/Web/API/History)
|
||||
- [Single-Page Applications (MDN)](https://developer.mozilla.org/en-US/docs/Glossary/SPA)
|
||||
Reference in New Issue
Block a user