Data Layer Setup in Umami
Umami's data layer is intentionally minimal and privacy-focused. Unlike traditional analytics platforms that encourage extensive user profiling, Umami's approach centers on event-level data collection without persistent user identification. This design philosophy means there's no traditional "data layer" object to populate with user attributes - instead, you work with event-specific data that's captured ephemerally and never tied to individual users across sessions.
Privacy-First Data Philosophy
What Umami Does NOT Track:
- Personal identifiable information (PII)
- Individual user identities across sessions
- IP addresses (stored or logged)
- User agents or device fingerprints
- Cookie-based user IDs
- Cross-session user journeys
What Umami DOES Track:
- Aggregated page views
- Event names and event-specific metadata
- Referrer sources (anonymized)
- Geographic data (country-level only)
- Device categories (mobile/desktop/tablet)
- Browser and OS (aggregated, not individual fingerprints)
This distinction is crucial: Umami is designed for understanding aggregate behavior patterns, not individual user profiling.
Event Data Structure
Umami's data layer revolves around custom events with optional metadata:
Basic Event Tracking:
// Simple event (no metadata)
umami.track('newsletter_signup');
// Event with metadata
umami.track('purchase', {
product: 'Pro Plan',
value: 99,
currency: 'USD',
category: 'subscription'
});
Event Data Properties:
umami.track('event_name', {
// String properties
plan_type: 'enterprise',
feature_name: 'export_data',
// Numeric properties
value: 149.99,
quantity: 3,
// Boolean properties
is_trial: false,
is_upgrade: true
});
Common Data Layer Patterns
E-Commerce Tracking
Product Views:
// Track when user views a product
umami.track('product_view', {
product_id: 'prod-123',
product_name: 'Wireless Headphones',
category: 'Electronics',
price: 79.99,
currency: 'USD'
});
Add to Cart:
umami.track('add_to_cart', {
product_id: 'prod-123',
product_name: 'Wireless Headphones',
quantity: 2,
price: 79.99,
cart_total: 159.98
});
Purchase Completion:
umami.track('purchase', {
transaction_id: 'txn-456789',
value: 159.98,
currency: 'USD',
items_count: 2,
payment_method: 'credit_card',
shipping_method: 'express'
});
Cart Abandonment:
// Track when user leaves checkout
umami.track('cart_abandoned', {
cart_value: 159.98,
items_count: 2,
checkout_step: 'payment'
});
SaaS Application Tracking
Feature Usage:
umami.track('feature_used', {
feature_name: 'data_export',
export_format: 'CSV',
record_count: 1500,
file_size_mb: 2.3
});
Subscription Events:
// Trial start
umami.track('trial_started', {
plan: 'pro',
trial_days: 14,
source: 'landing_page'
});
// Plan upgrade
umami.track('plan_upgraded', {
from_plan: 'basic',
to_plan: 'enterprise',
billing_cycle: 'annual',
value: 1200
});
// Subscription cancellation
umami.track('subscription_cancelled', {
plan: 'pro',
reason: 'too_expensive',
months_subscribed: 6
});
User Milestones:
umami.track('milestone_reached', {
milestone_type: 'projects_created',
milestone_value: 10,
days_since_signup: 45
});
Content Engagement
Video Tracking:
// Video started
umami.track('video_play', {
video_id: 'intro-tutorial',
video_title: 'Getting Started Guide',
video_duration: 180
});
// Video progress
umami.track('video_progress', {
video_id: 'intro-tutorial',
progress_percent: 50,
seconds_watched: 90
});
// Video completed
umami.track('video_complete', {
video_id: 'intro-tutorial',
total_watch_time: 180,
completion_rate: 100
});
Content Downloads:
umami.track('download', {
file_name: 'product-guide.pdf',
file_type: 'PDF',
file_size_mb: 2.5,
content_category: 'documentation'
});
Search Queries:
umami.track('search', {
query: 'integration guide',
results_count: 12,
filter_category: 'documentation'
});
Form Interactions
Form Submissions:
umami.track('form_submit', {
form_name: 'contact',
form_type: 'lead_generation',
fields_completed: 8,
time_to_complete: 120 // seconds
});
Form Errors:
umami.track('form_error', {
form_name: 'signup',
error_field: 'email',
error_type: 'invalid_format'
});
Form Abandonment:
umami.track('form_abandoned', {
form_name: 'checkout',
fields_completed: 3,
fields_total: 8,
completion_percent: 37.5
});
Data Layer Best Practices
1. Consistent Event Naming
Use a clear naming convention:
// Good: Descriptive, consistent
umami.track('newsletter_signup', { source: 'footer' });
umami.track('product_purchased', { category: 'electronics' });
umami.track('video_completed', { title: 'intro' });
// Bad: Inconsistent, unclear
umami.track('nlSignup', { source: 'footer' });
umami.track('bought', { category: 'electronics' });
umami.track('vidDone', { title: 'intro' });
Naming Convention Examples:
- Use snake_case:
cart_abandoned,signup_completed - Use verb + noun:
form_submitted,video_played,file_downloaded - Be specific:
trial_startednot justtrial - Group related events:
checkout_started,checkout_completed,checkout_abandoned
2. Structured Metadata
Keep metadata consistent across similar events:
// All purchase events should have same structure
umami.track('purchase', {
value: 99.99,
currency: 'USD',
category: 'subscription',
payment_method: 'stripe'
});
umami.track('purchase', {
value: 149.99,
currency: 'USD',
category: 'one_time',
payment_method: 'paypal'
});
3. Numeric Values for Analysis
Always include numeric values when tracking business metrics:
umami.track('goal_achieved', {
goal_type: 'revenue',
value: 500, // Always include value for aggregation
currency: 'USD',
source: 'email_campaign'
});
4. Avoid PII in Event Data
Never include personally identifiable information:
// Bad: Contains PII
umami.track('signup', {
email: 'user@example.com', // Don't do this
name: 'John Doe', // Don't do this
phone: '555-1234' // Don't do this
});
// Good: Generic metadata without PII
umami.track('signup', {
source: 'landing_page',
plan: 'free',
referrer_type: 'organic'
});
Integration with Tag Managers
If using Google Tag Manager or similar:
DataLayer Push to Umami:
// Listen to GTM dataLayer pushes
window.dataLayer = window.dataLayer || [];
window.dataLayer.push = function(obj) {
Array.prototype.push.call(window.dataLayer, obj);
// Convert GTM events to Umami events
if (obj.event && window.umami) {
const eventData = {
...obj
};
delete eventData.event; // Remove GTM event property
umami.track(obj.event, eventData);
}
};
Selective Event Forwarding:
// Only forward specific GTM events to Umami
const UMAMI_EVENTS = ['purchase', 'signup', 'download'];
window.dataLayer.push = function(obj) {
Array.prototype.push.call(window.dataLayer, obj);
if (obj.event && UMAMI_EVENTS.includes(obj.event) && window.umami) {
umami.track(obj.event, {
value: obj.value,
currency: obj.currency,
category: obj.category
});
}
};
Server-Side Data Collection
While Umami is primarily client-side, you can send events from your server:
Using Umami API:
// Node.js example
const fetch = require('node-fetch');
async function trackServerEvent(eventName, eventData) {
const response = await fetch('https://analytics.example.com/api/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'User-Agent': 'Mozilla/5.0' // Required
},
body: JSON.stringify({
type: 'event',
payload: {
website: 'YOUR-WEBSITE-ID',
url: '/api/endpoint', // Logical URL for server event
name: eventName,
data: eventData
}
})
});
return response.json();
}
// Track server-side purchase
await trackServerEvent('server_purchase', {
value: 99.99,
currency: 'USD',
payment_processor: 'stripe'
});
Data Validation and Testing
Test Event Tracking:
// Development environment: log events
function trackEvent(eventName, eventData) {
if (process.env.NODE_ENV === 'development') {
console.log('Umami Event:', eventName, eventData);
}
if (window.umami) {
umami.track(eventName, eventData);
}
}
// Use in your app
trackEvent('button_click', {
button_name: 'signup',
location: 'header'
});
Event Data Schema Validation:
// Define expected event schemas
const EVENT_SCHEMAS = {
purchase: ['value', 'currency', 'category'],
signup: ['source', 'plan'],
download: ['file_name', 'file_type']
};
function validateAndTrack(eventName, eventData) {
const schema = EVENT_SCHEMAS[eventName];
if (schema) {
const missingFields = schema.filter(field => !(field in eventData));
if (missingFields.length > 0) {
console.warn(`Missing fields for ${eventName}:`, missingFields);
}
}
umami.track(eventName, eventData);
}
Viewing Event Data in Dashboard
Access Event Data:
- Log into Umami dashboard
- Select your website
- Navigate to "Events" tab
- View event names, counts, and associated metadata
Analyzing Event Metadata:
- Click on specific event to see metadata breakdown
- Filter by date range to see trends
- Export data for external analysis
Limitations and Considerations
- Event data is stored according to your Umami instance settings
- Self-hosted: Configure retention in database settings
- Cloud: Check your plan's data retention policy
Metadata Limitations:
- Metadata is stored as JSON
- Very large metadata objects may impact performance
- Keep metadata focused and relevant
No User-Level Segmentation:
- Can't filter events by individual user characteristics
- All analysis is aggregate-level
- Use event metadata for segmentation (e.g., plan type, user role)
Query Capabilities:
- Limited complex querying in UI
- Use API for advanced analysis
- Export data to BI tools for deeper insights
Migration from Other Platforms
If migrating from Google Analytics or other platforms:
GA4 to Umami Event Mapping:
// GA4 event
gtag('event', 'purchase', {
transaction_id: 'T12345',
value: 99.99,
currency: 'USD',
items: [...]
});
// Umami equivalent (simplified)
umami.track('purchase', {
value: 99.99,
currency: 'USD'
// Note: Individual items not tracked (privacy-first)
});
Segment to Umami:
// Segment
analytics.track('Product Purchased', {
product_id: '123',
price: 99.99
});
// Umami
umami.track('product_purchased', {
price: 99.99
// Note: product_id removed to avoid tracking individual products per user
});
Summary
Umami's data layer is fundamentally different from traditional analytics platforms:
- No persistent user IDs: Events are anonymous and aggregated
- Event-centric model: Focus on what happens, not who does it
- Metadata for context: Use event properties for segmentation and analysis
- Privacy by design: Never include PII in event data
- Simple API: Easy to implement and maintain
This approach provides valuable business insights while respecting user privacy and simplifying compliance with data protection regulations.