Data Layer Overview
Lucky Orange's data layer allows you to enrich visitor sessions with custom information, identify specific users, and segment recordings based on visitor attributes. By implementing visitor identification and tagging, you can gain deeper insights into user behavior and create more targeted analysis.
Visitor Identification
Basic Identification
Identify visitors with custom properties to track individual users:
window._loq = window._loq || [];
_loq.push(['custom', {
name: 'John Doe',
email: 'john@example.com',
customer_id: '12345'
}]);
Complete Identification
Include comprehensive user data:
window._loq = window._loq || [];
_loq.push(['custom', {
// Identification
user_id: 'usr_abc123',
customer_id: '12345',
email: 'john.doe@example.com',
name: 'John Doe',
// Account Information
plan: 'professional',
account_type: 'business',
signup_date: '2024-01-15',
// Behavioral Data
lifetime_value: 4999.99,
total_orders: 15,
last_login: '2024-03-20',
// Demographics
company: 'Acme Corporation',
industry: 'Technology',
location: 'San Francisco, CA'
}]);
When to Identify
Call custom at these key moments:
- After Login: Identify user immediately after authentication
- On Page Load: For returning authenticated users
- After Registration: Capture new user information
- Profile Updates: When user updates their information
// After successful login
function handleLoginSuccess(user) {
_loq.push(['custom', {
user_id: user.id,
email: user.email,
name: user.fullName,
plan: user.subscription.plan
}]);
}
// On page load for authenticated users
if (window.currentUser) {
_loq.push(['custom', {
user_id: currentUser.id,
email: currentUser.email
}]);
}
Visitor Tagging
Basic Tags
Tag visitors to categorize and filter sessions:
window._loq = window._loq || [];
// Add single tag
_loq.push(['tag', 'VIP Customer']);
// Add multiple tags
_loq.push(['tag', 'Returning Visitor']);
_loq.push(['tag', 'Premium Plan']);
_loq.push(['tag', 'High Value']);
Dynamic Tagging
Tag based on user behavior or attributes:
// Tag based on subscription level
if (user.plan === 'enterprise') {
_loq.push(['tag', 'Enterprise Customer']);
} else if (user.plan === 'pro') {
_loq.push(['tag', 'Pro Customer']);
}
// Tag based on cart value
if (cartTotal > 500) {
_loq.push(['tag', 'High Value Cart']);
}
// Tag based on user actions
document.getElementById('discount-code').addEventListener('apply', function() {
_loq.push(['tag', 'Used Discount Code']);
});
Conditional Tags
// Tag by visitor lifecycle stage
function tagVisitorStage(user) {
if (user.isNew) {
_loq.push(['tag', 'New Visitor']);
} else if (user.daysSinceSignup < 30) {
_loq.push(['tag', 'Recent Signup']);
} else {
_loq.push(['tag', 'Established Customer']);
}
}
// Tag by engagement level
if (user.loginCount > 50) {
_loq.push(['tag', 'Power User']);
} else if (user.loginCount > 10) {
_loq.push(['tag', 'Active User']);
}
Use Cases
E-commerce Visitor Data
window._loq = window._loq || [];
// Identify customer
_loq.push(['custom', {
customer_id: order.customerId,
email: order.email,
name: order.customerName,
lifetime_value: customer.totalSpent,
total_orders: customer.orderCount,
last_purchase_date: customer.lastOrderDate
}]);
// Tag customer segments
_loq.push(['tag', 'Customer Segment: ' + customer.segment]);
// Tag by cart status
if (cart.items.length > 0) {
_loq.push(['tag', 'Active Cart']);
_loq.push(['custom', {
cart_value: cart.total,
cart_items: cart.items.length
}]);
}
SaaS Application Data
// User identification
_loq.push(['custom', {
user_id: user.id,
email: user.email,
company: user.company.name,
plan: user.subscription.plan,
mrr: user.subscription.mrr,
trial_end_date: user.trialEndsAt,
feature_flags: user.enabledFeatures
}]);
// Tag by user status
_loq.push(['tag', 'User Type: ' + user.type]);
if (user.isTrial) {
_loq.push(['tag', 'Trial User']);
}
if (user.isAdmin) {
_loq.push(['tag', 'Admin User']);
}
Lead Scoring
let leadScore = calculateLeadScore(visitor);
_loq.push(['custom', {
lead_score: leadScore,
traffic_source: visitor.source,
pages_viewed: visitor.pageViews,
time_on_site: visitor.sessionDuration
}]);
// Tag by lead quality
if (leadScore > 80) {
_loq.push(['tag', 'Hot Lead']);
} else if (leadScore > 50) {
_loq.push(['tag', 'Warm Lead']);
} else {
_loq.push(['tag', 'Cold Lead']);
}
Data Layer Best Practices
Naming Conventions
Use clear, consistent property names:
// Good - descriptive and consistent
_loq.push(['custom', {
user_id: '123',
email_address: 'user@example.com',
subscription_plan: 'professional',
account_created_date: '2024-01-01'
}]);
// Avoid - inconsistent and vague
_loq.push(['custom', {
uid: '123',
mail: 'user@example.com',
plan: 'professional',
created: '2024-01-01'
}]);
Data Privacy
Avoid sending sensitive information:
// Safe - no sensitive data
_loq.push(['custom', {
user_id: user.id,
email: user.email, // Generally safe
plan: user.plan
}]);
// Unsafe - sensitive data
_loq.push(['custom', {
password: user.password, // NEVER send
credit_card: user.ccNumber, // NEVER send
ssn: user.socialSecurity // NEVER send
}]);
Performance Considerations
// Good - single call with all data
_loq.push(['custom', {
user_id: user.id,
email: user.email,
name: user.name,
plan: user.plan
}]);
// Less efficient - multiple calls
_loq.push(['custom', { user_id: user.id }]);
_loq.push(['custom', { email: user.email }]);
_loq.push(['custom', { name: user.name }]);
_loq.push(['custom', { plan: user.plan }]);
Viewing Data in Dashboard
Accessing Custom Data
- Go to Recordings in Lucky Orange dashboard
- Select a recording to view
- Custom data appears in the Visitor Info panel
- Tags appear as labels on recordings
Filtering by Custom Data
- Navigate to Recordings → Filters
- Filter by Tags to find specific segments
- Search for specific email or user ID
- Filter by custom properties (plan-dependent)
Visitor Profiles
- Click Visitors in main navigation
- View individual visitor profiles
- See all custom data for each visitor
- View complete session history
Advanced Implementations
Dynamic Data Layer
// Initialize data layer object
window.dataLayer = window.dataLayer || [];
// Function to sync with Lucky Orange
function syncToLuckyOrange(data) {
window._loq = window._loq || [];
if (data.user) {
_loq.push(['custom', {
user_id: data.user.id,
email: data.user.email,
name: data.user.name
}]);
}
if (data.tags && Array.isArray(data.tags)) {
data.tags.forEach(tag => {
_loq.push(['tag', tag]);
});
}
}
// Update data and sync
dataLayer.push({
user: currentUser,
tags: ['Premium', 'Authenticated']
});
syncToLuckyOrange(dataLayer[dataLayer.length - 1]);
Event-Based Identification
// Identify after specific events
document.addEventListener('userLoggedIn', function(e) {
_loq.push(['custom', {
user_id: e.detail.userId,
email: e.detail.email
}]);
_loq.push(['tag', 'Logged In']);
});
document.addEventListener('subscriptionUpgraded', function(e) {
_loq.push(['custom', {
plan: e.detail.newPlan,
upgrade_date: new Date().toISOString()
}]);
_loq.push(['tag', 'Recently Upgraded']);
});
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| Custom data not appearing | Called before Lucky Orange loaded | Ensure _loq array initialization |
| Tags not showing on recordings | Tag not pushed correctly | Verify tag syntax ['tag', 'TagName'] |
| Data overwritten | Multiple custom calls | Data merges, but verify all properties sent |
| Special characters breaking | Invalid characters in data | Sanitize data before sending |
| Too much data | Sending large objects | Limit to essential properties only |
Integration Examples
With JavaScript Frameworks
import { useEffect } from 'react';
function App() {
const user = useCurrentUser();
useEffect(() => {
if (user && window._loq) {
window._loq.push(['custom', {
user_id: user.id,
email: user.email,
plan: user.plan
}]);
}
}, [user]);
return <div>Your App</div>;
}
Vue:
export default {
mounted() {
if (this.$user && window._loq) {
window._loq.push(['custom', {
user_id: this.$user.id,
email: this.$user.email
}]);
}
}
}
Summary
- Identify Users: Use
customto add user properties - Tag Sessions: Use
tagto categorize visitors - Timing Matters: Identify after login or on page load
- Privacy First: Never send sensitive data
- Be Consistent: Use standard naming conventions
- Filter & Analyze: Use tags to segment recordings
- Test Thoroughly: Verify data appears in dashboard