Overview
The data layer is a JavaScript object that holds information about your page, users, and events. When using Google Tag Manager (GTM) with FullStory, a properly configured data layer enables you to:
- Send user properties from GTM to FullStory
- Track custom events based on data layer changes
- Dynamically identify users without hardcoding FullStory calls
- Centralize your tracking logic
Data Layer Basics
What is a Data Layer?
The data layer is a global JavaScript array that stores structured data:
window.dataLayer = window.dataLayer || [];
You push data into it like this:
dataLayer.push({
'event': 'user_login',
'userId': 'user_12345',
'userEmail': 'user@example.com',
'userPlan': 'premium'
});
GTM listens to the data layer and fires tags based on what you push.
Setting Up Data Layer for FullStory
Step 1: Initialize Data Layer (Before GTM)
Place this before your GTM container snippet:
<head>
<script>
window.dataLayer = window.dataLayer || [];
</script>
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){...GTM snippet...})(window,document,'script','dataLayer','GTM-XXXX');</script>
<!-- End Google Tag Manager -->
</head>
Step 2: Push User Data to Data Layer
When a user logs in or when user data is available:
dataLayer.push({
'event': 'user_identified',
'userId': 'user_12345',
'userEmail': 'user@example.com',
'userPlan': 'premium',
'userSignupDate': '2024-01-15',
'userCompany': 'Acme Corp'
});
Step 3: Create GTM Variables
In Google Tag Manager, create variables to access data layer values:
- Variables > New > Data Layer Variable
- Create these variables:
- Data Layer Variable Name:
userId→ GTM Variable Name:DL - User ID - Data Layer Variable Name:
userEmail→ GTM Variable Name:DL - User Email - Data Layer Variable Name:
userPlan→ GTM Variable Name:DL - User Plan - Data Layer Variable Name:
userSignupDate→ GTM Variable Name:DL - Signup Date
- Data Layer Variable Name:
Step 4: Create FullStory Identify Tag
Create a Custom HTML tag in GTM that calls FS.identify():
Tag Configuration:
<script>
if (window.FS && {{DL - User ID}}) {
FS.identify({{DL - User ID}}, {
email_str: {{DL - User Email}},
plan_str: {{DL - User Plan}},
signupDate_str: {{DL - Signup Date}}
});
}
</script>
Trigger: Custom Event user_identified
Result: When user_identified event is pushed to the data layer, FullStory automatically identifies the user.
Tracking Custom Events via Data Layer
Step 1: Push Events to Data Layer
// When user clicks a button
dataLayer.push({
'event': 'button_click',
'buttonName': 'Sign Up CTA',
'buttonLocation': 'homepage',
'buttonColor': 'blue'
});
// When user completes purchase
dataLayer.push({
'event': 'purchase_complete',
'orderId': 'order_789',
'revenue': 149.99,
'itemCount': 3
});
Step 2: Create GTM Trigger
In GTM:
- Triggers > New
- Trigger Type: Custom Event
- Event Name:
button_click - Save
Step 3: Create FullStory Event Tag
Tag Configuration:
<script>
if (window.FS) {
FS.event({{Event}}, {
buttonName: {{DL - Button Name}},
buttonLocation: {{DL - Button Location}},
buttonColor: {{DL - Button Color}}
});
}
</script>
Trigger: Custom Event button_click
Variables to create:
DL - Button Name(Data Layer Variable:buttonName)DL - Button Location(Data Layer Variable:buttonLocation)DL - Button Color(Data Layer Variable:buttonColor)
E-Commerce Data Layer Integration
Enhanced E-Commerce Data Layer
If you use GTM's Enhanced E-Commerce, you can send product data to FullStory:
// Product view
dataLayer.push({
'event': 'productView',
'ecommerce': {
'detail': {
'products': [{
'id': 'P12345',
'name': 'Widget Pro',
'price': '49.99',
'brand': 'Acme',
'category': 'Electronics'
}]
}
}
});
// Add to cart
dataLayer.push({
'event': 'addToCart',
'ecommerce': {
'add': {
'products': [{
'id': 'P12345',
'name': 'Widget Pro',
'price': '49.99',
'quantity': 1
}]
}
}
});
// Purchase
dataLayer.push({
'event': 'purchase',
'ecommerce': {
'purchase': {
'actionField': {
'id': 'T12345',
'revenue': '149.99',
'shipping': '5.00',
'tax': '10.00'
},
'products': [{
'id': 'P12345',
'name': 'Widget Pro',
'price': '49.99',
'quantity': 1
}]
}
}
});
Send E-Commerce Events to FullStory
GTM Tag for Product View:
<script>
if (window.FS && {{ecommerce.detail.products.0.id}}) {
FS.event('Product Viewed', {
product_id: {{ecommerce.detail.products.0.id}},
product_name: {{ecommerce.detail.products.0.name}},
product_price: {{ecommerce.detail.products.0.price}}
});
}
</script>
Trigger: Custom Event productView
Advanced Data Layer Patterns
User Properties Update
When user properties change (e.g., subscription upgrade):
dataLayer.push({
'event': 'user_properties_updated',
'userId': 'user_12345',
'userPlan': 'enterprise', // Updated from 'premium'
'upgradedAt': new Date().toISOString()
});
GTM Tag to sync to FullStory:
<script>
if (window.FS && {{DL - User ID}}) {
FS.identify({{DL - User ID}}, {
plan_str: {{DL - User Plan}},
upgradedAt_str: {{DL - Upgraded At}}
});
}
</script>
Page Metadata
Send page-level information:
dataLayer.push({
'pageCategory': 'product',
'pageSubcategory': 'electronics',
'contentAuthor': 'John Doe',
'publishDate': '2024-01-15'
});
Track in FullStory as custom user vars:
<script>
if (window.FS) {
FS.setVars('page', {
category: {{DL - Page Category}},
subcategory: {{DL - Page Subcategory}},
author: {{DL - Content Author}}
});
}
</script>
SPA (Single Page Application) Data Layer
For React, Vue, Angular apps with client-side routing:
Virtual Page Views
// On route change
dataLayer.push({
'event': 'virtualPageview',
'pagePath': '/dashboard',
'pageTitle': 'User Dashboard'
});
GTM Tag:
<script>
if (window.FS) {
FS.event('Page View', {
path: {{Page Path}},
title: {{Page Title}}
});
}
</script>
Trigger: Custom Event virtualPageview
Best Practices
Use Consistent Naming
Good:
dataLayer.push({
'event': 'button_click',
'buttonName': 'Sign Up',
'buttonLocation': 'header'
});
Bad:
dataLayer.push({
'event': 'click',
'btn': 'Sign Up',
'loc': 'header'
});
Push Events, Not Just Data
Always include an event key when you want to trigger tags:
// Good - triggers GTM tags
dataLayer.push({
'event': 'user_login',
'userId': '12345'
});
// Bad - won't trigger tags
dataLayer.push({
'userId': '12345'
});
Avoid PII in Data Layer
Don't push sensitive data like full credit card numbers or SSNs:
// Bad
dataLayer.push({
'creditCard': '4111111111111111',
'ssn': '123-45-6789'
});
// Good
dataLayer.push({
'paymentMethod': 'visa',
'lastFourDigits': '1111'
});
Test in GTM Preview Mode
Before publishing:
- Preview mode in GTM
- Navigate your site
- Verify data layer pushes trigger FullStory tags
- Check that FullStory events appear correctly
Troubleshooting
Common Data Layer Issues
| Issue | Symptoms | Possible Causes | Solutions |
|---|---|---|---|
| Events not firing | Data layer pushes but FullStory events don't appear | - FullStory not loaded - GTM trigger misconfigured - Tag not firing - Event name mismatch |
- Check typeof FS === 'function'- Verify trigger event name (case-sensitive) - Use GTM Preview mode to debug - Check tag firing conditions |
| User not identified | Events appear but not linked to user | - User ID not a string - FS.identify() tag not firing- User ID is null or undefined- Identify fires after events |
- Convert user ID to string: userId.toString()- Check GTM trigger for identify tag - Validate user ID exists before pushing - Ensure identify runs before events |
| Variables undefined | GTM variables show as undefined |
- Data layer key name mismatch - Variable not set before tag fires - Typo in variable name - Data pushed after page load |
- Match exact key names (case-sensitive) - Push data before GTM loads - Check for typos in variable configuration - Use dataLayer.push() not reassignment |
| GTM tag not firing | Tag doesn't appear in Preview mode | - Trigger conditions not met - Tag paused or disabled - Firing order wrong - Exception blocking tag |
- Review trigger conditions in GTM - Enable tag if paused - Check tag sequencing/priority - Remove or adjust exceptions |
| Property type errors | Properties not showing correctly in FullStory | - Missing type suffixes (_str, _int, etc.)- Wrong type for value - Invalid property format |
- Add _str, _int, _real, _bool, _date suffixes- Convert values to correct type - Review FullStory property naming rules |
| E-commerce data not tracking | Enhanced e-commerce pushes but events missing | - FullStory tag not listening to e-commerce events - Product data structure incorrect - Missing GTM triggers for e-commerce |
- Create separate tags for each e-commerce event - Validate data structure matches Enhanced E-commerce spec - Set up triggers for productView, addToCart, etc. |
| Data layer state issues | Previous values appearing in new events | - Data layer not clearing between pushes - Variables persisting across events |
- Push undefined to clear values: {key: undefined}- Use event-scoped variables - Avoid relying on persisted state |
| SPA route changes not tracking | Virtual pageviews missing in FullStory | - Route change events not pushed - History API not tracked - GTM not listening to route changes |
- Push virtualPageview event on route change- Use GTM History Change trigger - Implement router hooks to push events |
| Timing issues | Events fire before FullStory loads | - Data layer pushes too early - FullStory loads after events - Race condition |
- Wrap FullStory calls in if (window.FS) check- Queue events until FullStory ready - Use GTM tag sequencing |
| Integration with other tags | Conflicts with GA4, Segment, or other analytics | - Multiple tags using same data layer - Variable name conflicts - Event name collisions |
- Use namespaced event names - Check tag firing order - Coordinate variable naming across tags |
Events Not Firing
Step-by-step debugging:
Verify data layer push:
// In console, check data layer console.log(window.dataLayer); // Monitor new pushes const originalPush = window.dataLayer.push; window.dataLayer.push = function() { console.log('Data layer push:', arguments[0]); return originalPush.apply(this, arguments); };Check GTM trigger:
- Open GTM Preview mode
- Navigate to your site
- Look for your custom event in the Summary
- If not firing: review trigger configuration
Verify FullStory loaded:
console.log(typeof FS); // Should return "function"Check tag execution:
- In GTM Preview, click on your event
- Check "Tags Fired" section
- If tag didn't fire: review firing conditions
Inspect Network requests:
- Open DevTools > Network
- Filter by
fullstory - Trigger your event
- Look for POST to
/rec/bundle
User Not Identified
Common issues and fixes:
Issue: User ID not a string
// Incorrect - Number
dataLayer.push({ userId: 12345 });
// Correct - String
dataLayer.push({ userId: '12345' });
// Or
dataLayer.push({ userId: String(12345) });
Issue: Identify tag not firing
- Check GTM trigger is set to custom event
user_identified - Verify trigger conditions don't have conflicting rules
- Ensure tag priority is set correctly
Issue: User properties missing type suffixes
// Incorrect
FS.identify(userId, {
email: user.email, // Missing _str
plan: user.plan // Missing _str
});
// Correct
FS.identify(userId, {
email_str: user.email,
plan_str: user.plan
});
Variable Configuration Issues
Verify GTM variables:
- Go to Variables in GTM
- Check User-Defined Variables
- For each data layer variable:
- Variable Type: "Data Layer Variable"
- Data Layer Variable Name: exact key from
dataLayer.push() - Example: If you push
{ userId: '123' }, variable name isuserId
Test variables in Preview mode:
- Enable GTM Preview
- Navigate to page
- Click on event in Summary
- Go to Variables tab
- Check Data Layer Variables section
- Verify values are correct
Debugging Data Layer in GTM Preview
Step 1: Enable Preview Mode
Step 2: Monitor Data Layer
- In debug panel, click on any event
- Go to Data Layer tab
- Expand each object to see all keys/values
Step 3: Check Tag Firing
- Select your custom event from timeline
- Check "Tags Fired" section
- If FullStory tag is there: it fired successfully
- If in "Tags Not Fired": review why
Step 4: Inspect Variables
- Click on your event
- Go to Variables tab
- Find your data layer variables
- Verify values match what you pushed
Best Practices for Debugging
Add console logging:
// Log all data layer pushes
function logDataLayerPush(data) {
console.log('[Data Layer]', data);
dataLayer.push(data);
}
// Usage
logDataLayerPush({
event: 'button_click',
buttonName: 'Sign Up'
});
Validate before pushing:
function pushToDataLayer(data) {
// Validate required fields
if (!data.event) {
console.error('Missing event key:', data);
return;
}
// Validate user ID is string if present
if (data.userId && typeof data.userId !== 'string') {
console.warn('User ID should be string:', data.userId);
data.userId = String(data.userId);
}
dataLayer.push(data);
}
Test in staging first:
- Always test data layer changes in staging
- Use GTM Preview mode to validate
- Check FullStory dashboard for events
- Verify user identification works
- Only then deploy to production
GTM Container Issues
Container not loading:
- Verify GTM snippet in
<head>section - Check GTM container ID is correct
- Ensure no JavaScript errors blocking GTM
Container published but changes not live:
- Hard refresh browser (Cmd+Shift+R or Ctrl+Shift+R)
- Clear browser cache
- Verify correct container version is live
- Check GTM Preview shows latest version
Multiple containers on same page:
- Can cause conflicts
- Use separate container IDs
- Coordinate data layer naming
Getting Help
If issues persist:
- GTM Community: support.google.com/tagmanager
- FullStory Support: support@fullstory.com
- Stack Overflow: Tag with
google-tag-managerandfullstory
Include in support requests:
- GTM container ID (if safe to share)
- Data layer push code
- GTM Preview screenshots
- Console errors
- Expected vs actual behavior
Next Steps:
- Event Tracking - Track custom events
- Cross-Domain Tracking - Track across domains
- Install or Embed Tag - Deploy FullStory
Additional Resources: