
Company News
Socket Has Acquired Secure Annex
Socket has acquired Secure Annex to expand extension security across browsers, IDEs, and AI tools.
@deriv-com/analytics
Advanced tools
Comprehensive analytics package for Deriv applications. Provides unified event tracking, A/B testing, and user analytics through RudderStack, PostHog and GrowthBook integrations with built-in caching and offline support.
A modern, tree-shakeable analytics library for tracking user events with RudderStack and PostHog. Designed for optimal performance with advanced caching, batching, and offline support.
Note: GrowthBook support is deprecated and will be removed in a future major version. For A/B testing and feature flags, we recommend using PostHog's built-in feature flag capabilities.
# Using npm
npm install @deriv-com/analytics
# Using yarn
yarn add @deriv-com/analytics
# Using pnpm
pnpm add @deriv-com/analytics
Core dependencies (@rudderstack/analytics-js, js-cookie, and posthog-js) are installed automatically.
Use directly in browsers without a build tool:
<!-- Load from jsdelivr CDN -->
<script src="https://cdn.jsdelivr.net/npm/@deriv-com/analytics@latest/dist/browser/analytics.bundle.global.js"></script>
<script>
const { Analytics } = window.DerivAnalytics
Analytics.initialise({
rudderstackKey: 'YOUR_RUDDERSTACK_KEY',
posthogOptions: {
apiKey: 'YOUR_POSTHOG_KEY',
config: {
autocapture: true,
},
},
}).then(() => {
Analytics.trackEvent('page_view', { page: 'home' })
})
</script>
Bundle Size: ~380 KB minified / ~125 KB gzipped (includes RudderStack + PostHog + all dependencies)
import { Analytics } from '@deriv-com/analytics'
// Initialize with RudderStack
await Analytics.initialise({
rudderstackKey: 'YOUR_RUDDERSTACK_KEY',
})
// Track events
Analytics.trackEvent('ce_virtual_signup_form', {
action: 'signup_done',
signup_provider: 'email',
})
// Track page views
Analytics.pageView('/dashboard', 'Deriv App')
// Identify users
Analytics.identifyEvent('CR123456')
import { Analytics } from '@deriv-com/analytics'
await Analytics.initialise({
// RudderStack for event tracking (required)
rudderstackKey: 'YOUR_RUDDERSTACK_KEY',
// PostHog for analytics and session recording (optional)
posthogOptions: {
apiKey: 'phc_YOUR_POSTHOG_KEY',
config: {
session_recording: {
recordCrossOriginIframes: true,
minimumDurationMilliseconds: 30000,
},
autocapture: true,
},
},
})
// Events are automatically sent to both providers
Analytics.trackEvent('ce_login_form', {
action: 'login_cta',
login_provider: 'google',
})
// User identification syncs with both providers
// When using PostHog, pass email via provider-specific traits (see User Identification section)
Analytics.identifyEvent('CR123456', {
rudderstack: { language: 'en', country_of_residence: 'US' },
posthog: { email: 'user@example.com', language: 'en', country_of_residence: 'US' },
})
The recommended pattern is a single useAnalytics hook that handles initialization and exposes all tracking methods:
// hooks/useAnalytics.ts
import { useEffect } from 'react'
import { Analytics } from '@deriv-com/analytics'
let isInitialized = false
export function useAnalytics() {
useEffect(() => {
if (isInitialized) return
isInitialized = true
const rudderstackKey = process.env.REACT_APP_RUDDERSTACK_KEY // ← replace with your env var
const posthogKey = process.env.REACT_APP_POSTHOG_KEY // ← replace with your env var
if (!rudderstackKey && !posthogKey) return
Analytics.initialise({
...(rudderstackKey && { rudderstackKey }),
...(posthogKey && {
posthogOptions: {
apiKey: posthogKey,
api_host: process.env.REACT_APP_POSTHOG_HOST,
},
}),
debug: process.env.NODE_ENV === 'development',
})
}, [])
return {
trackEvent: Analytics.trackEvent,
identifyEvent: Analytics.identifyEvent,
pageView: Analytics.pageView,
loadEvent: Analytics.loadEvent,
setAttributes: Analytics.setAttributes,
reset: Analytics.reset,
}
}
Call the hook once at the top of your app:
// App.tsx
import { useAnalytics } from './hooks/useAnalytics'
function App() {
const { trackEvent } = useAnalytics()
return <button onClick={() => trackEvent('ce_signup_button', { action: 'click' })}>Sign Up</button>
}
Use the same useAnalytics hook (with NEXT_PUBLIC_ env var prefix) inside a dedicated client provider:
// hooks/useAnalytics.ts
'use client'
import { useEffect } from 'react'
import { Analytics } from '@deriv-com/analytics'
let isInitialized = false
export function useAnalytics() {
useEffect(() => {
if (isInitialized) return
isInitialized = true
const rudderstackKey = process.env.NEXT_PUBLIC_RUDDERSTACK_KEY // ← replace with your env var
const posthogKey = process.env.NEXT_PUBLIC_POSTHOG_KEY // ← replace with your env var
if (!rudderstackKey && !posthogKey) return
Analytics.initialise({
...(rudderstackKey && { rudderstackKey }),
...(posthogKey && {
posthogOptions: {
apiKey: posthogKey,
api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST,
},
}),
debug: process.env.NODE_ENV === 'development',
})
}, [])
return {
trackEvent: Analytics.trackEvent,
identifyEvent: Analytics.identifyEvent,
pageView: Analytics.pageView,
loadEvent: Analytics.loadEvent,
setAttributes: Analytics.setAttributes,
reset: Analytics.reset,
}
}
// app/analytics-provider.tsx
'use client'
import { useAnalytics } from '@/hooks/useAnalytics'
export function AnalyticsProvider({ children }: { children: React.ReactNode }) {
useAnalytics()
return <>{children}</>
}
// app/layout.tsx
import { AnalyticsProvider } from './analytics-provider'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<AnalyticsProvider>{children}</AnalyticsProvider>
</body>
</html>
)
}
// pages/_app.tsx
import { useAnalytics } from '../hooks/useAnalytics'
import type { AppProps } from 'next/app'
export default function App({ Component, pageProps }: AppProps) {
useAnalytics()
return <Component {...pageProps} />
}
// main.ts or main.js
import { createApp } from 'vue'
import { Analytics } from '@deriv-com/analytics'
import App from './App.vue'
// Initialize analytics
Analytics.initialise({
rudderstackKey: import.meta.env.VITE_RUDDERSTACK_KEY,
posthogOptions: {
apiKey: import.meta.env.VITE_POSTHOG_KEY,
},
})
// Make Analytics available globally
const app = createApp(App)
app.config.globalProperties.$analytics = Analytics
app.mount('#app')
// Usage in components
export default {
methods: {
handleClick() {
this.$analytics.trackEvent('button_clicked', { button_name: 'submit' })
},
},
}
<!doctype html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/@deriv-com/analytics@latest/dist/browser/analytics.bundle.global.js"></script>
</head>
<body>
<button id="signup-btn">Sign Up</button>
<script>
const { Analytics } = window.DerivAnalytics
// Initialize
Analytics.initialise({
rudderstackKey: 'YOUR_KEY',
posthogOptions: {
apiKey: 'YOUR_POSTHOG_KEY',
},
})
// Track button clicks
document.getElementById('signup-btn').addEventListener('click', () => {
Analytics.trackEvent('ce_signup_button', {
action: 'click',
location: 'header',
})
})
</script>
</body>
</html>
RudderStack is used for event tracking and includes performance optimizations:
await Analytics.initialise({
rudderstackKey: 'YOUR_RUDDERSTACK_KEY',
})
Built-in Performance Features:
navigator.sendBeacon for better performance on page unloadPostHog provides powerful analytics, session recording, and feature flags:
await Analytics.initialise({
rudderstackKey: 'YOUR_RUDDERSTACK_KEY',
posthogOptions: {
// Required: API key
apiKey: 'phc_YOUR_KEY',
// Optional: Override the PostHog API host.
// If omitted, the host is auto-selected at init time based on window.location.hostname:
// *.deriv.me → https://ph.deriv.me
// *.deriv.be → https://ph.deriv.be
// *.deriv.ae → https://ph.deriv.ae
// all others → https://ph.deriv.com (default, also used in SSR/non-browser environments)
// Set this explicitly if you need to override the resolved host (e.g. in tests or custom deployments).
api_host: 'https://ph.deriv.com',
// Optional: PostHog configuration
config: {
// ui_host controls where the PostHog UI links point (e.g. session replay links).
// This is separate from api_host and should remain pointed at the PostHog cloud UI.
ui_host: 'https://us.posthog.com',
// Session recording
session_recording: {
recordCrossOriginIframes: true,
maskAllInputs: false,
minimumDurationMilliseconds: 30000, // Only save sessions longer than 30 seconds
},
// Feature capture
autocapture: true, // Automatically capture clicks, form submissions, etc.
capture_pageview: true, // Automatically capture page views
capture_pageleave: true, // Capture when users leave pages
// Console log recording (useful for debugging)
enable_recording_console_log: true,
// Disable features if needed
disable_session_recording: false,
disable_surveys: false,
// Custom event filtering
before_send: event => {
// Custom logic to filter or modify events
return event
},
},
},
})
On every PostHog initialization, the library automatically removes leftover ph_*_posthog cookies from previous or rotated API keys. This prevents stale cookies from accumulating in users' browsers when the PostHog project key changes.
PostHog events are only sent from the following domains (hardcoded internally):
deriv.com, deriv.be, deriv.me, deriv.team, deriv.aelocalhost and 127.0.0.1 are always allowedEvents from any other domain are silently blocked. This list is not user-configurable.
posthogOptions: {
apiKey: 'phc_YOUR_KEY',
config: {
session_recording: {
// Record content from iframes
recordCrossOriginIframes: true,
// Mask sensitive input fields
maskAllInputs: true,
maskInputOptions: {
password: true,
email: true,
},
// Only save sessions longer than 1 minute
minimumDurationMilliseconds: 60000,
// Sampling (record only 50% of sessions)
sessionRecordingSampleRate: 0.5,
},
},
}
Initialize the analytics instance before tracking events:
await Analytics.initialise({
rudderstackKey: 'YOUR_RUDDERSTACK_KEY',
posthogOptions: {
apiKey: 'phc_YOUR_POSTHOG_KEY',
config: {
autocapture: true,
},
},
debug: false, // Enable to log all analytics calls to the console
})
Track custom events with any payload — there are no enforced property types. Send exactly what your event needs:
Analytics.trackEvent('ce_login_form', {
action: 'login_cta',
login_provider: 'email',
form_name: 'main_login',
})
Analytics.trackEvent('ce_signup_form', {
action: 'signup_done',
signup_provider: 'google',
cta_information: {
cta_name: 'get_started',
section_name: 'hero',
},
})
Identify users and sync traits across analytics providers:
// Identify user with ID only
Analytics.identifyEvent('CR123456')
// Send same traits to both RudderStack and PostHog (no PostHog-specific fields)
Analytics.identifyEvent('CR123456', {
language: 'en',
country_of_residence: 'US',
account_type: 'real',
})
// Send provider-specific traits (recommended when using PostHog)
// PostHog requires `email` to automatically compute the `is_internal` flag
Analytics.identifyEvent('CR123456', {
rudderstack: {
language: 'en',
custom_field: 'value',
},
posthog: {
email: 'user@example.com', // Required for PostHog — used to set is_internal flag
language: 'en',
country_of_residence: 'US',
},
})
How it works:
{ language: 'en' }), the same traits are sent to both providersrudderstack or posthog keys, provider-specific traits are usedemail is provided in PostHog traits, the is_internal flag is automatically computed and set as a person property — email itself is not forwarded to PostHogTrack page navigation:
// Basic page view
Analytics.pageView('/dashboard')
// With custom platform name
Analytics.pageView('/dashboard', 'Deriv Trader')
// With additional properties
Analytics.pageView('/trade', 'Deriv App', {
section: 'multipliers',
instrument: 'forex',
})
Note: PostHog automatically captures page views when capture_pageview: true is set in config. Manual page view tracking is primarily for RudderStack.
Set user and context attributes that are automatically included in all subsequent events. Pass any key-value pairs — no fixed schema is enforced:
Analytics.setAttributes({
country: 'US',
user_language: 'en',
account_type: 'real',
device_type: 'mobile',
account_currency: 'USD',
account_mode: 'demo',
residence_country: 'US',
loggedIn: true,
// any additional fields your app needs
})
Clear user session from all providers (e.g., on logout):
Analytics.reset()
The package includes automatic caching to ensure no events are lost — no extra configuration needed.
When you call trackEvent or pageView before initialise() completes, events are stored in localStorage and replayed automatically once the SDK loads:
// Safe to call before initialise() — automatically replayed on load
Analytics.trackEvent('button_clicked', { button: 'submit' })
Analytics.pageView('/dashboard')
When the user is offline but the SDK is already initialized, events are held in memory and flushed on the next online trackEvent call:
// While offline — queued in memory, sent automatically when back online
Analytics.trackEvent('offline_event', { data: 'cached' })
Fire events only on specific pages using loadEvent:
Analytics.loadEvent([
{
pages: ['dashboard', 'profile'],
event: { name: 'ce_page_load', properties: { page_type: 'authenticated' } },
},
{
excludedPages: ['login'],
event: { name: 'ce_authenticated_view', properties: {} },
},
])
Enable verbose logging to trace every analytics call in the browser console:
await Analytics.initialise({
rudderstackKey: 'YOUR_KEY',
posthogOptions: { apiKey: 'phc_YOUR_KEY' },
debug: true,
})
All logs are prefixed with [ANALYTIC] (e.g., [ANALYTIC][RudderStack] trackEvent | ...). Useful during development and QA to verify events are firing correctly without opening the network tab.
Each provider can be used independently for maximum flexibility:
import { Posthog } from '@deriv-com/analytics/posthog'
const posthog = Posthog.getPosthogInstance({
apiKey: 'phc_YOUR_KEY',
config: {
autocapture: true,
session_recording: {
recordCrossOriginIframes: true,
},
},
})
// Track events
posthog.capture('button_clicked', { button_name: 'signup' })
// Identify users — email is required and used to compute is_internal
posthog.identifyEvent('CR123', { email: 'user@example.com', language: 'en' })
// Check feature flags
const isEnabled = posthog.isFeatureEnabled('new-feature')
const variant = posthog.getFeatureFlag('button-color')
import { RudderStack } from '@deriv-com/analytics/rudderstack'
const rudderstack = RudderStack.getRudderStackInstance('YOUR_KEY', () => {
console.log('RudderStack loaded')
})
// Track events
rudderstack.track('button_clicked', { button: 'signup' })
// Identify users
rudderstack.identifyEvent('CR123', { language: 'en' })
// Track page views
rudderstack.pageView('/dashboard', 'Deriv App', 'CR123')
Access raw provider instances for advanced use cases:
const { tracking, posthog } = Analytics.getInstances()
// Access PostHog directly
if (posthog?.has_initialized) {
posthog.capture('custom_event', { property: 'value' })
// Access PostHog feature flags
const isEnabled = posthog.isFeatureEnabled('new-feature')
}
// Access RudderStack directly
if (tracking?.has_initialized) {
const userId = tracking.getUserId()
const anonId = tracking.getAnonymousId()
}
initialise(options: Options): Promise<void>Initialize the analytics instance.
Parameters:
interface Options {
rudderstackKey?: string
posthogOptions?: {
apiKey: string
/**
* Optional PostHog API host. If omitted, resolved automatically based on window.location.hostname:
* *.deriv.me → https://ph.deriv.me
* *.deriv.be → https://ph.deriv.be
* *.deriv.ae → https://ph.deriv.ae
* all others → https://ph.deriv.com (default; also used server-side)
*/
api_host?: string
config?: PostHogConfig
}
/** Enable verbose debug logging — all analytics calls are logged prefixed with [ANALYTIC] */
debug?: boolean
}
trackEvent(event: string, payload: Record<string, any>): voidTrack an event. No payload schema is enforced — send any key-value pairs.
pageView(url: string, platform?: string, properties?: Record<string, unknown>): voidTrack page navigation.
identifyEvent(userId?: string, traits?: Record<string, any>): voidLink anonymous session to a user ID with optional traits. When PostHog is active and traits include an email field (via provider-specific posthog key), is_internal is automatically computed and set as a person property — the email itself is not stored in PostHog.
backfillPersonProperties({ user_id, email?, country_of_residence? }): voidBackfills PostHog person properties for users identified in previous sessions. Sets client_id and is_internal if they are not already present. No-op if PostHog is not initialized or user_id is empty.
// Call after PostHog has loaded and user ID is available
Analytics.backfillPersonProperties({ user_id: 'CR123456', email: 'user@example.com', country_of_residence: 'US' })
setAttributes(attributes: Record<string, any>): voidUpdate user attributes that flow to all providers. No schema is enforced.
loadEvent(items: PageLoadEventConfig[]): voidFire events conditionally based on the current page pathname.
type PageLoadEventConfig = {
pages?: string[] // fire only on these pages
excludedPages?: string[] // fire on all pages except these
event: { name: string; properties: Record<string, any> }
callback?: () => { name: string; properties: Record<string, any> }
}
reset(): voidClear user session from all providers.
getId(): stringGet the current user ID.
getAnonymousId(): stringGet the anonymous user ID.
getInstances(): { tracking, posthog }Access raw provider instances.
navigator.sendBeacon for reliable event delivery on page unloadEstimated sizes (minified + gzipped):
rudderstackKey is correctAnalytics.getInstances().tracking.has_initialized in consolephc_)allowedDomains listAnalytics.getInstances().posthog?.has_initialized in consoleph.deriv.com or your PostHog hostdisable_session_recording: false (or omit it)minimumDurationMilliseconds are not savednavigator.onLine in consoleAnalytics.getInstances().tracking.has_initializedcached_analytics_events and cached_analytics_page_views keys. The rudder_anonymous_id is still stored as a cookie.Analytics.reset()// Old (v1.x) - hardcoded traits
Analytics.identifyEvent('CR123')
// New (v2.x) - custom traits
Analytics.identifyEvent('CR123', {
language: 'en',
country_of_residence: 'US',
})
// Or provider-specific
Analytics.identifyEvent('CR123', {
rudderstack: { language: 'en' },
posthog: { language: 'en', country_of_residence: 'US' },
})
// Old (GrowthBook)
const isEnabled = Analytics.isFeatureOn('new-feature')
// New (PostHog)
const { posthog } = Analytics.getInstances()
const isEnabled = posthog?.isFeatureEnabled('new-feature')
Contributions are welcome! Please follow these guidelines:
npm test and npm run buildMIT
For issues and questions:
FAQs
Comprehensive analytics package for Deriv applications. Provides unified event tracking, A/B testing, and user analytics through RudderStack, PostHog and GrowthBook integrations with built-in caching and offline support.
The npm package @deriv-com/analytics receives a total of 4,525 weekly downloads. As such, @deriv-com/analytics popularity was classified as popular.
We found that @deriv-com/analytics demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
Socket has acquired Secure Annex to expand extension security across browsers, IDEs, and AI tools.

Research
/Security News
Socket is tracking cloned Open VSX extensions tied to GlassWorm, with several updated from benign-looking sleepers into malware delivery vehicles.

Product
Reachability analysis for PHP is now available in experimental, helping teams identify which vulnerabilities are actually exploitable.