@datalyr/web
v1.4.1
Published
Datalyr Web SDK - Modern attribution tracking for web applications
Maintainers
Readme
@datalyr/web
Browser SDK for event tracking, user identity, and attribution. Version 1.4.1.
Table of Contents
- Installation
- Quick Start
- How It Works
- Configuration
- API Reference
- Web-to-App Attribution
- SPA Support
- Framework Integration
- TypeScript
- Troubleshooting
- License
Installation
NPM
npm install @datalyr/webScript Tag
<script defer src="https://track.datalyr.com/dl.js"
data-workspace-id="YOUR_WORKSPACE_ID"></script>The script tag loads externally (zero bundle size) and auto-initializes. Access the SDK via window.datalyr.
Choose one installation method. Using both causes conflicts.
| Feature | Script Tag | NPM |
|---------|-----------|-----|
| Bundle Size | 0 (external) | ~15KB |
| TypeScript | No | Yes |
| Access | window.datalyr | import datalyr |
| Initialize | Automatic | datalyr.init() |
Quick Start
import datalyr from '@datalyr/web';
datalyr.init({
workspaceId: 'YOUR_WORKSPACE_ID'
});
// Wait for async initialization (encryption, container, initial page view)
await datalyr.ready();
// Track events
datalyr.track('button_clicked', { button: 'signup' });
// Identify users
datalyr.identify('user_123', { email: '[email protected]' });
// Track page views (page name goes in properties)
datalyr.page({ title: 'Pricing', variant: 'A' });How It Works
- Events are created with
track(),page(),identify(), etc. - Each event includes device context, session data, and attribution parameters.
- Events are queued locally and sent in batches.
- If the browser is offline, events are stored and sent when connectivity returns.
- Events are processed server-side for analytics and attribution reporting.
Event Payload
Every event includes:
{
event_name: 'purchase',
event_data: { ... }, // Custom + attribution + session properties
// Identity
anonymous_id: 'anon_xxxxx', // Persistent browser ID
distinct_id: 'user_123', // user_id if identified, else anonymous_id
user_id: 'user_123', // Set after identify()
session_id: 'sess_xxxxx', // Current session
// Context (in event_data)
url: 'https://example.com/pricing',
title: 'Pricing',
referrer: 'https://google.com',
// Attribution (in event_data, if captured)
utm_source: 'facebook',
fbclid: 'abc123',
// Metadata
workspace_id: 'wk_xxxxx',
source: 'web',
timestamp: '2024-01-15T10:30:00Z',
sdk_version: '1.4.1',
sdk_name: 'datalyr-web-sdk'
}Configuration
All properties of DatalyrConfig:
datalyr.init({
// Required
workspaceId: string,
// Endpoint
endpoint?: string, // Default: 'https://ingest.datalyr.com'
fallbackEndpoints?: string[], // Default: [] - Additional endpoints to try on failure
// Debugging
debug?: boolean, // Default: false - Enable console logging
// Batching
batchSize?: number, // Default: 10 - Events per batch
flushInterval?: number, // Default: 5000 - Flush interval in ms
flushAt?: number, // Default: 10 - Flush when N events queued
// Priority events (bypass normal batching)
criticalEvents?: string[], // Default: undefined (disabled). Events flushed immediately.
highPriorityEvents?: string[], // Default: undefined (disabled). Events given queue priority.
// Session
sessionTimeout?: number, // Default: 3600000 (60 min)
trackSessions?: boolean, // Default: true
// Attribution
attributionWindow?: number, // Default: 7776000000 (90 days)
trackedParams?: string[], // Default: [] - Additional URL params to capture
// Privacy
respectDoNotTrack?: boolean, // Default: false - Honor browser DNT header
respectGlobalPrivacyControl?: boolean, // Default: true - Honor GPC signal
privacyMode?: 'standard' | 'strict', // Default: 'standard' - 'strict' limits data collection
// Cookies / Storage
cookieDomain?: string | 'auto', // Default: 'auto'
cookieExpires?: number, // Default: 365 (days)
secureCookie?: boolean | 'auto', // Default: 'auto'
sameSite?: 'Strict' | 'Lax' | 'None', // Default: 'Lax'
cookiePrefix?: string, // Default: '__dl_'
// Performance
enablePerformanceTracking?: boolean, // Default: true - Collect navigation timing metrics
enableFingerprinting?: boolean, // Default: true - Minimal device fingerprinting (standard mode only)
// Retry / Offline
maxRetries?: number, // Default: 5
retryDelay?: number, // Default: 1000 (ms)
maxOfflineQueueSize?: number, // Default: 100
// SPA
trackSPA?: boolean, // Default: true - Auto-track route changes
trackPageViews?: boolean, // Default: true - Auto-track initial page view
// Container scripts
enableContainer?: boolean, // Default: true - Load third-party scripts from dashboard
// Auto-Identify (opt-in)
autoIdentify?: boolean, // Default: false - Automatically identify users
autoIdentifyForms?: boolean, // Default: true - Capture email from forms (when autoIdentify enabled)
autoIdentifyAPI?: boolean, // Default: true - Capture email from API responses (when autoIdentify enabled)
autoIdentifyShopify?: boolean, // Default: true - Capture email from Shopify endpoints (when autoIdentify enabled)
autoIdentifyTrustedDomains?: string[], // Default: [] - Additional domains to trust for API capture
// Plugins
plugins?: DatalyrPlugin[], // Default: [] - Custom plugin instances
});API Reference
Event Tracking
track(eventName, properties?)
Track a custom event.
track(eventName: string, properties?: EventProperties): voiddatalyr.track('signup_started');
datalyr.track('product_viewed', {
product_id: 'SKU123',
product_name: 'Blue Shirt',
price: 29.99,
currency: 'USD',
category: 'Apparel',
});page(properties?)
Track a page view. The page title, url, path, search, and referrer are captured automatically. Pass properties to add or override values.
page(properties?: PageProperties): void// Track current page (all properties auto-captured)
datalyr.page();
// Override title, add custom properties
datalyr.page({ title: 'Pricing', variant: 'A' });
// Add custom properties
datalyr.page({ product_id: 'SKU123', source: 'search' });Note: page() does not accept a name string as the first argument. To set the page name, pass it as title in the properties object.
screen(screenName, properties?)
Track a screen view. Intended for SPAs or hybrid apps.
screen(screenName: string, properties?: Record<string, any>): voiddatalyr.screen('Dashboard');
datalyr.screen('Product Details', { product_id: 'SKU123' });Internally fires a pageview event with the screen property set.
trackAppDownloadClick(options)
Track an app download click, then redirect to the app store. Fires a $app_download_click event with full attribution, flushes via sendBeacon, and redirects.
trackAppDownloadClick(options: {
targetPlatform: 'ios' | 'android';
appStoreUrl: string;
}): voiddatalyr.trackAppDownloadClick({
targetPlatform: 'ios',
appStoreUrl: 'https://apps.apple.com/app/your-app/id123456789',
});For Android Play Store URLs, attribution parameters are automatically encoded into the referrer query parameter for deterministic install attribution.
User Identity
identify(userId, traits?)
Link the anonymous visitor to a known user. Rotates the session ID on call (prevents session fixation). User traits are encrypted at rest.
identify(userId: string, traits?: UserTraits): voiddatalyr.identify('user_123', {
email: '[email protected]',
name: 'John Doe',
plan: 'premium',
company: 'Acme Inc',
});alias(userId, previousId?)
Create an alias linking one user ID to another. If previousId is omitted, the current anonymous ID is used.
alias(userId: string, previousId?: string): voiddatalyr.alias('user_123', 'temp_user_456');group(groupId, traits?)
Associate the current user with a group or account.
group(groupId: string, traits?: Record<string, any>): voiddatalyr.group('company_abc', { name: 'Acme Inc', plan: 'enterprise' });reset()
Clear user identity and start a new session. The anonymous ID is preserved.
datalyr.reset();getAnonymousId()
Returns the persistent anonymous ID assigned on first visit.
getAnonymousId(): stringgetUserId()
Returns the current user ID set by identify(), or null if not identified.
getUserId(): string | nullgetDistinctId()
Returns the distinct ID. This is userId if identified, otherwise anonymousId.
getDistinctId(): stringSession
getSessionId()
Returns the current session ID.
getSessionId(): stringstartNewSession()
Force-start a new session. Returns the new session ID.
startNewSession(): stringconst newSessionId = datalyr.startNewSession();getSessionData()
Returns the current session metadata, or null if no active session.
getSessionData(): SessionData | nullinterface SessionData {
id: string;
startTime: number;
lastActivity: number;
pageViews: number;
events: number;
duration: number;
isActive: boolean;
}ready()
Returns a promise that resolves when async initialization is complete (encryption, container loading, initial page view).
ready(): Promise<void>datalyr.init({ workspaceId: 'YOUR_WORKSPACE_ID' });
await datalyr.ready();
// Encryption is initialized, container is loaded, safe to trackAttribution
getAttribution()
Returns the current captured attribution data.
getAttribution(): Attributionconst attribution = datalyr.getAttribution();
console.log(attribution.source, attribution.medium, attribution.campaign);interface Attribution {
source?: string | null;
medium?: string | null;
campaign?: string | null;
term?: string | null;
content?: string | null;
clickId?: string | null;
clickIdType?: string | null; // 'fbclid', 'gclid', etc.
referrer?: string | null;
referrerHost?: string | null;
landingPage?: string | null;
landingPath?: string | null;
timestamp?: number;
[key: string]: any; // Custom tracked params
}Captured automatically from URLs:
| Type | Parameters |
|------|------------|
| UTM | utm_source, utm_medium, utm_campaign, utm_content, utm_term |
| Click IDs | fbclid, gclid, ttclid, twclid, li_fat_id, msclkid |
| Referrer | referrer, landing_page |
Attribution supports first-touch (90-day window), last-touch, and journey tracking.
setAttribution(attribution)
Manually set or override attribution data.
setAttribution(attribution: Partial<Attribution>): voiddatalyr.setAttribution({
source: 'partner',
medium: 'referral',
campaign: 'spring_promo',
});getJourney()
Returns the customer journey as an array of touchpoints (up to 30).
getJourney(): TouchPoint[]interface TouchPoint {
timestamp: number;
source?: string;
medium?: string;
campaign?: string;
sessionId: string;
}Super Properties
Super properties are included with every subsequent event.
setSuperProperties(properties)
Merge properties into the super properties store.
setSuperProperties(properties: Record<string, any>): voiddatalyr.setSuperProperties({ app_version: '2.1.0', environment: 'production' });unsetSuperProperty(propertyName)
Remove a single super property by key.
unsetSuperProperty(propertyName: string): voiddatalyr.unsetSuperProperty('environment');getSuperProperties()
Returns a copy of the current super properties.
getSuperProperties(): Record<string, any>Privacy and Consent
optOut()
Opt the user out of all tracking. Clears the event queue and persists the preference in a cookie.
datalyr.optOut();optIn()
Opt the user back in.
datalyr.optIn();isOptedOut()
Returns true if the user has opted out.
isOptedOut(): booleansetConsent(consent)
Set granular consent preferences.
setConsent(consent: ConsentConfig): voiddatalyr.setConsent({
analytics: true,
marketing: false,
preferences: true,
sale: false // CCPA "Do Not Sell"
});interface ConsentConfig {
analytics?: boolean;
marketing?: boolean;
preferences?: boolean;
sale?: boolean;
}Privacy Modes
// Standard mode (default) - normal fingerprinting + tracking
datalyr.init({ workspaceId: '...', privacyMode: 'standard' });
// Strict mode - minimal data collection, no fingerprinting
datalyr.init({ workspaceId: '...', privacyMode: 'strict' });The SDK also respects respectDoNotTrack and respectGlobalPrivacyControl config flags.
Queue and Network
flush()
Manually flush the event queue. Returns a promise that resolves when all queued events are sent.
flush(): Promise<void>await datalyr.flush();getNetworkStatus()
Returns the current network status as tracked by the SDK.
getNetworkStatus(): NetworkStatusinterface NetworkStatus {
isOnline: boolean;
lastOfflineAt: number | null;
lastOnlineAt: number | null;
}Events are automatically stored when offline and sent when connectivity returns.
Container Scripts
Container scripts manage third-party tracking pixels (Meta, Google, TikTok) configured in your Datalyr dashboard.
loadScript(scriptId)
Trigger a container script by ID.
loadScript(scriptId: string): voiddatalyr.loadScript('custom-script-id');getLoadedScripts()
Returns an array of loaded script IDs.
getLoadedScripts(): string[]Container script tag installation (loads pixels from your dashboard config):
<script defer src="https://track.datalyr.com/container.js"
data-workspace-id="YOUR_WORKSPACE_ID"></script>Debugging
getErrors()
Returns an array of SDK errors (up to 50 most recent).
getErrors(): ErrorInfo[]interface ErrorInfo {
message: string;
stack?: string;
context?: any;
timestamp: string;
url: string;
}console.log(datalyr.getErrors());Enable debug: true in config to log all SDK activity to the console.
Lifecycle
destroy()
Tear down the SDK instance. Restores patched history.pushState / replaceState, removes event listeners, cleans up the queue, session, container iframes, auto-identify listeners, and encryption keys. Resets all in-memory state.
destroy(): voiddatalyr.destroy();Web-to-App Attribution
Track users who click a "Download App" button on your website and attribute their app install back to the original web session.
Flow
- User clicks an ad and lands on your page (web SDK captures
fbclid, UTMs, etc.) - User clicks "Download App" --
trackAppDownloadClick()fires with full attribution - User installs from App Store / Play Store
- First app open -- mobile SDK recovers the web attribution:
- Android: Deterministic match via Play Store
referrerparameter (~95% accuracy) - iOS: IP-based match against recent web events within 24 hours (~90%+ accuracy)
- Android: Deterministic match via Play Store
Usage
document.querySelector('#download-btn').addEventListener('click', () => {
datalyr.trackAppDownloadClick({
targetPlatform: 'ios',
appStoreUrl: 'https://apps.apple.com/app/your-app/id123456789',
});
});// Android - attribution params auto-encoded into Play Store referrer
datalyr.trackAppDownloadClick({
targetPlatform: 'android',
appStoreUrl: 'https://play.google.com/store/apps/details?id=com.yourapp',
});Requirements
- Web SDK initialized on the prelander page
- Mobile SDK (
@datalyr/react-nativeor@datalyr/swift) installed in the app - Attribution is recovered automatically on first app launch
SPA Support
Route changes are tracked automatically when trackSPA: true (the default). The SDK patches history.pushState, history.replaceState, and listens for popstate and hashchange events. Attribution cache is cleared on each navigation so new URL parameters are captured.
datalyr.init({
workspaceId: 'YOUR_WORKSPACE_ID',
trackSPA: true, // default
trackPageViews: true, // auto-track initial page view, default
});
// Manual page tracking
datalyr.page({ title: 'Product Details', product_id: 'SKU-123' });Framework Integration
React
import { useEffect } from 'react';
import datalyr from '@datalyr/web';
function App() {
useEffect(() => {
datalyr.init({ workspaceId: 'YOUR_WORKSPACE_ID' });
}, []);
const handleClick = () => {
datalyr.track('button_clicked', { button_name: 'CTA' });
};
return <button onClick={handleClick}>Click Me</button>;
}Vue
<script setup>
import { onMounted } from 'vue';
import datalyr from '@datalyr/web';
onMounted(() => {
datalyr.init({ workspaceId: 'YOUR_WORKSPACE_ID' });
});
const trackClick = () => {
datalyr.track('button_clicked');
};
</script>
<template>
<button @click="trackClick">Click Me</button>
</template>Next.js
// app/providers.tsx
'use client';
import { useEffect } from 'react';
import datalyr from '@datalyr/web';
export function AnalyticsProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
datalyr.init({
workspaceId: process.env.NEXT_PUBLIC_DATALYR_WORKSPACE_ID!,
debug: process.env.NODE_ENV === 'development',
});
}, []);
return <>{children}</>;
}
// app/layout.tsx
import { AnalyticsProvider } from './providers';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<AnalyticsProvider>{children}</AnalyticsProvider>
</body>
</html>
);
}TypeScript
All types are exported from the package:
import datalyr from '@datalyr/web';
import type {
DatalyrConfig,
EventProperties,
UserTraits,
PageProperties,
SessionData,
Attribution,
TouchPoint,
ConsentConfig,
DatalyrPlugin,
FingerprintData,
IngestEventPayload,
IngestBatchPayload,
NetworkStatus,
ErrorInfo,
PerformanceMetrics,
} from '@datalyr/web';const properties: EventProperties = {
product_id: 'SKU-123',
price: 99.99,
quantity: 2,
};
datalyr.track('product_added', properties);
const traits: UserTraits = {
email: '[email protected]',
name: 'John Doe',
plan: 'premium',
};
datalyr.identify('user_123', traits);Plugin Interface
interface DatalyrPlugin {
name: string;
initialize(datalyr: any): void;
page?(properties: PageProperties): void;
track?(eventName: string, properties: EventProperties): void;
identify?(userId: string, traits: UserTraits): void;
loaded?(): void;
}Troubleshooting
Events not appearing
- Verify
workspaceIdis correct. - Set
debug: trueand check the console. - Confirm
datalyr.getAnonymousId()returns a value. - Check the Network tab for requests to
ingest.datalyr.com. - Disable ad blockers or privacy extensions.
SDK not initialized
console.log('Anonymous ID:', datalyr.getAnonymousId());
console.log('Session ID:', datalyr.getSessionId());
console.log('User ID:', datalyr.getUserId());
console.log('Network:', datalyr.getNetworkStatus());
console.log('Errors:', datalyr.getErrors());If these return undefined, the SDK is not initialized. Call datalyr.init() first.
Async initialization
init() returns synchronously, but encryption and container loading happen asynchronously. If you need to ensure everything is ready before tracking:
datalyr.init({ workspaceId: '...' });
await datalyr.ready();
datalyr.track('post_init_event');Next.js SSR
SDK methods only work in the browser. Always call them inside useEffect or other client-side hooks:
'use client';
import { useEffect } from 'react';
import datalyr from '@datalyr/web';
useEffect(() => {
datalyr.init({ workspaceId: '...' });
}, []);Script tag vs NPM
Use one installation method, not both. Using both creates duplicate instances.
License
MIT
