tessl install tessl/npm-posthog-js@1.335.0PostHog Browser JS Library is a comprehensive browser analytics and feature management SDK that enables developers to capture user events, track product analytics, manage feature flags, record session replays, and implement feedback mechanisms like surveys and conversations in web applications.
Event capture allows you to track user actions and behaviors. You can capture custom events with properties, register super properties that apply to all events, and manage event properties dynamically.
Captures a custom event with optional properties and configuration options.
/**
* Captures a custom event
* @param event - Event name
* @param properties - Event properties (optional)
* @param options - Capture options (optional)
* @returns CaptureResult if successful, void otherwise
*/
function capture(
event: EventName,
properties?: Properties,
options?: CaptureOptions
): CaptureResult | void;Usage Examples:
// Simple event
posthog.capture('button_clicked');
// Event with properties
posthog.capture('purchase_completed', {
product_id: '12345',
price: 99.99,
currency: 'USD'
});
// Event with options
posthog.capture('important_action',
{ action_type: 'critical' },
{ send_instantly: true } // Don't batch this event
);
// Set user properties with event
posthog.capture('user_action', {}, {
$set: { plan: 'premium' },
$set_once: { first_purchase_date: '2024-01-15' }
});Registers properties that will be sent with every subsequent event.
/**
* Registers super properties that apply to all events
* @param properties - Properties to register
* @param days - Number of days to persist (optional)
*/
function register(properties: Properties, days?: number): void;Usage Example:
// Register properties for all events
posthog.register({
app_version: '2.1.0',
environment: 'production'
});
// Register with expiration
posthog.register({
promotion_code: 'SUMMER2024'
}, 30); // Expires in 30 daysRegisters properties only if they don't already exist.
/**
* Registers super properties once if not already set
* @param properties - Properties to register
* @param default_value - Default value if property exists
* @param days - Number of days to persist (optional)
*/
function register_once(
properties: Properties,
default_value?: Property,
days?: number
): void;Usage Example:
// Register only on first visit
posthog.register_once({
initial_referrer: document.referrer,
initial_landing_page: window.location.pathname
});Registers properties for the current session only (cleared on new session).
/**
* Registers properties for the current session only
* @param properties - Properties to register for this session
*/
function register_for_session(properties: Properties): void;Usage Example:
// Properties only for this session
posthog.register_for_session({
session_campaign: 'email_newsletter',
session_variant: 'A'
});Removes a registered super property.
/**
* Removes a registered super property
* @param property - Property name to remove
*/
function unregister(property: string): void;Usage Example:
posthog.unregister('promotion_code');Removes a session-specific property.
/**
* Removes a session-specific property
* @param property - Property name to remove
*/
function unregister_for_session(property: string): void;Usage Example:
posthog.unregister_for_session('session_campaign');Retrieves the value of a registered super property.
/**
* Gets the value of a registered super property
* @param property_name - Property name
* @returns Property value or undefined if not found
*/
function get_property(property_name: string): Property | undefined;Usage Example:
const appVersion = posthog.get_property('app_version');
console.log('Current version:', appVersion);Retrieves the value of a session-specific property.
/**
* Gets the value of a session-specific property
* @param property_name - Property name
* @returns Property value or undefined if not found
*/
function getSessionProperty(property_name: string): Property | undefined;Usage Example:
const campaign = posthog.getSessionProperty('session_campaign');Calculates the combined properties for an event, including super properties.
/**
* Calculates combined event properties including super properties
* @param properties - Base event properties
* @param eventName - Event name for context
* @param readOnly - If true, don't modify internal state
* @returns Combined properties object
*/
function calculateEventProperties(
properties?: Properties,
eventName?: string,
readOnly?: boolean
): Properties;Usage Example:
// Preview what properties will be sent
const finalProps = posthog.calculateEventProperties({
custom_prop: 'value'
}, 'my_event', true);
console.log('Event will include:', finalProps);Registers a callback that fires whenever an event is captured.
/**
* Registers an event listener
* @param event - Event type (currently only 'eventCaptured')
* @param cb - Callback function
* @returns Function to unsubscribe
*/
function on(event: 'eventCaptured', cb: (data: CaptureResult) => void): () => void;Usage Example:
// Listen to all captured events
const unsubscribe = posthog.on('eventCaptured', (data) => {
console.log('Event captured:', data.event, data.properties);
});
// Later, stop listening
unsubscribe();Gets the unique ID for the current page view.
/**
* Gets the unique ID for the current page view
* @returns Page view ID or undefined
*/
function getPageViewId(): string | undefined;Usage Example:
const pageViewId = posthog.getPageViewId();
console.log('Current page view:', pageViewId);Adds an item to the execution queue for snippet compatibility.
/**
* Pushes an item to the execution queue
* Used for snippet-style initialization and command queuing
* @param item - Command array to execute
*/
function push(item: any[]): void;Usage Example:
// Snippet-style command queuing
posthog.push(['capture', 'event_name', { property: 'value' }]);
posthog.push(['identify', 'user-123']);
// Typically used with the PostHog snippet for async loading
window.posthog = window.posthog || [];
window.posthog.push(['init', 'api-key']);Returns a string representation of the PostHog instance.
/**
* Returns a string representation of the PostHog instance
* @returns String representation
*/
function toString(): string;Usage Example:
console.log(posthog.toString());
// Output: "posthog" or instance name/**
* Event name - any string value
*/
type EventName = string;/**
* A single property value
*/
type Property = string | number | boolean | null | undefined;/**
* Object containing event or user properties
*/
type Properties = Record<string, Property | Property[]>;/**
* Result of capturing an event
*/
interface CaptureResult {
/**
* Event name that was captured
*/
event: string;
/**
* Properties that were sent with the event
*/
properties: Record<string, any>;
}/**
* Options for capturing events
*/
interface CaptureOptions {
/**
* Send this event immediately without batching
* @default false
*/
send_instantly?: boolean;
/**
* Custom timestamp for the event
* @default Current time
*/
timestamp?: Date;
/**
* User properties to set with this event
*/
$set?: Properties;
/**
* User properties to set once with this event
*/
$set_once?: Properties;
/**
* Internal batch key for grouping events
* @internal
*/
_batchKey?: string;
}PostHog reserves certain event names for built-in functionality. Do not use these names for custom events:
// Reserved event names (automatically captured by PostHog)
type ReservedEventName =
| '$pageview' // Automatic page view tracking
| '$pageleave' // Page leave events
| '$autocapture' // Automatically captured interactions
| '$identify' // User identification
| '$create_alias' // User aliasing
| '$set' // Set user properties
| '$set_once' // Set user properties once
| '$groupidentify' // Group identification
| '$feature_flag_called' // Feature flag evaluation
| '$feature_flag_call' // Alternative name for flag evaluation
| '$survey_shown' // Survey shown to user
| '$survey_sent' // Survey response submitted
| '$survey_dismissed' // Survey dismissed by user
| '$exception' // Exception captured
| '$web_vitals' // Web performance metrics
| '$ai_feedback' // LLM trace feedback
| '$ai_metric' // LLM trace metric
| '$session_start' // Session started
| '$session_end' // Session ended
| '$opt_in' // User opted in
| '$opt_out'; // User opted outNote: Avoid event names starting with $ to prevent conflicts with PostHog's internal events.
PostHog automatically captures certain events based on configuration:
Automatically captured when capture_pageview is enabled:
// Captured automatically
{
event: '$pageview',
properties: {
$current_url: 'https://example.com/page',
$host: 'example.com',
$pathname: '/page',
// ... other properties
}
}Automatically captured when capture_pageleave is enabled:
// Captured automatically when user leaves page
{
event: '$pageleave',
properties: {
$current_url: 'https://example.com/page',
// ... other properties
}
}When autocapture is enabled, PostHog automatically captures:
// Captured automatically on click
{
event: '$autocapture',
properties: {
$event_type: 'click',
$el_text: 'Sign Up',
$el_tag: 'button',
// ... other element properties
}
}// Deduplicate rapid events
class EventDeduplicator {
private recentEvents = new Map<string, number>();
private dedupWindow = 1000; // 1 second
capture(event: string, properties: Properties = {}) {
const key = this.getEventKey(event, properties);
const now = Date.now();
const lastTime = this.recentEvents.get(key);
if (lastTime && (now - lastTime) < this.dedupWindow) {
console.log('Skipping duplicate event:', event);
return;
}
this.recentEvents.set(key, now);
posthog.capture(event, properties);
// Cleanup old entries
this.cleanup();
}
private getEventKey(event: string, properties: Properties): string {
return `${event}:${JSON.stringify(properties)}`;
}
private cleanup() {
const now = Date.now();
for (const [key, time] of this.recentEvents.entries()) {
if (now - time > this.dedupWindow * 2) {
this.recentEvents.delete(key);
}
}
}
}
const deduplicator = new EventDeduplicator();
// Usage
button.addEventListener('click', () => {
deduplicator.capture('button_clicked', { button_id: 'signup' });
});// Validate events before sending
interface EventSchema {
required: string[];
optional?: string[];
types?: Record<string, string>;
}
const eventSchemas: Record<string, EventSchema> = {
'purchase_completed': {
required: ['order_id', 'total', 'currency'],
optional: ['discount', 'items'],
types: {
order_id: 'string',
total: 'number',
currency: 'string'
}
}
};
function captureValidatedEvent(
event: string,
properties: Properties
): CaptureResult | void {
const schema = eventSchemas[event];
if (schema) {
// Check required properties
const missing = schema.required.filter(key => !(key in properties));
if (missing.length > 0) {
console.error(`Missing required properties for ${event}:`, missing);
return;
}
// Validate types
if (schema.types) {
for (const [key, expectedType] of Object.entries(schema.types)) {
if (key in properties && typeof properties[key] !== expectedType) {
console.error(`Wrong type for ${key}: expected ${expectedType}`);
return;
}
}
}
}
return posthog.capture(event, properties);
}
// Usage
captureValidatedEvent('purchase_completed', {
order_id: '12345',
total: 99.99,
currency: 'USD'
});// Automatically enrich all events
const enrichmentRules = [
// Add user context
(properties: Properties) => ({
...properties,
user_plan: posthog.get_property('plan'),
user_cohort: posthog.get_property('cohort')
}),
// Add session context
(properties: Properties) => ({
...properties,
session_id: posthog.get_session_id(),
page_url: window.location.href
}),
// Add device context
(properties: Properties) => ({
...properties,
screen_resolution: `${window.screen.width}x${window.screen.height}`,
viewport_size: `${window.innerWidth}x${window.innerHeight}`,
device_memory: (navigator as any).deviceMemory,
connection_type: (navigator as any).connection?.effectiveType
})
];
function captureEnriched(event: string, properties: Properties = {}) {
let enriched = properties;
for (const rule of enrichmentRules) {
enriched = rule(enriched);
}
return posthog.capture(event, enriched);
}// Track async operations with timing
async function trackAsyncOperation<T>(
operationName: string,
operation: () => Promise<T>,
context: Properties = {}
): Promise<T> {
const startTime = Date.now();
const eventId = `${operationName}-${Date.now()}`;
// Track start
posthog.capture(`${operationName}_started`, {
...context,
event_id: eventId
});
try {
const result = await operation();
const duration = Date.now() - startTime;
// Track success
posthog.capture(`${operationName}_completed`, {
...context,
event_id: eventId,
duration_ms: duration,
success: true
});
return result;
} catch (error) {
const duration = Date.now() - startTime;
// Track failure
posthog.capture(`${operationName}_failed`, {
...context,
event_id: eventId,
duration_ms: duration,
error: error instanceof Error ? error.message : String(error)
});
throw error;
}
}
// Usage
const userData = await trackAsyncOperation(
'fetch_user_data',
() => fetchUserData(userId),
{ user_id: userId }
);// Debug event capture
posthog.on('eventCaptured', (data) => {
console.log('Event captured locally:', data.event);
console.log('Properties:', data.properties);
});
// Check if capturing is enabled
console.log('Is capturing:', posthog.is_capturing());
// Check opt-out status
console.log('Has opted out:', posthog.has_opted_out_capturing());
// Enable debug mode
posthog.debug(true);
// Try capturing test event
const result = posthog.capture('test_event', { test: true });
console.log('Capture result:', result);// Check super properties
console.log('Registered properties:', {
app_version: posthog.get_property('app_version'),
environment: posthog.get_property('environment')
});
// Check session properties
console.log('Session property:', posthog.getSessionProperty('campaign'));
// Debug property calculation
const properties = posthog.calculateEventProperties(
{ custom: 'value' },
'test_event',
true // readOnly mode
);
console.log('Final properties:', properties);// Check before_send hook
posthog.init('token', {
before_send: (event) => {
console.log('Before send:', event);
// Filter sensitive events
if (event.properties.sensitive) {
console.log('Blocked sensitive event');
return null;
}
return event;
}
});// Send critical events immediately
posthog.capture('payment_completed', {
order_id: '12345',
amount: 99.99
}, {
send_instantly: true // Don't wait for batch
});
// Regular events use batching
posthog.capture('page_viewed', {
path: window.location.pathname
});// Throttle scroll events
let scrollTimeout: NodeJS.Timeout;
window.addEventListener('scroll', () => {
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(() => {
posthog.capture('scroll_depth', {
depth: window.scrollY,
max_depth: document.body.scrollHeight - window.innerHeight
});
}, 500); // Only capture after 500ms of no scrolling
});// Sample high-frequency events
function captureSampled(
event: string,
properties: Properties,
sampleRate: number = 0.1
): CaptureResult | void {
if (Math.random() < sampleRate) {
return posthog.capture(event, {
...properties,
sampled: true,
sample_rate: sampleRate
});
}
}
// Capture 10% of scroll events
window.addEventListener('scroll', () => {
captureSampled('scrolled', {
scroll_position: window.scrollY
}, 0.1);
});// Mock for unit tests
const mockCapture = jest.fn();
(window as any).posthog = {
capture: mockCapture,
identify: jest.fn(),
// ... other methods
};
// Test event capture
it('captures event on button click', () => {
render(<Button />);
fireEvent.click(screen.getByRole('button'));
expect(mockCapture).toHaveBeenCalledWith(
'button_clicked',
expect.objectContaining({
button_id: 'test-button'
})
);
});// Test helper to verify event shape
function expectEventCaptured(
eventName: string,
expectedProperties: Partial<Properties>
) {
const calls = (posthog.capture as jest.Mock).mock.calls;
const matchingCall = calls.find(call => call[0] === eventName);
expect(matchingCall).toBeDefined();
expect(matchingCall[1]).toMatchObject(expectedProperties);
}
// Usage
fireEvent.click(button);
expectEventCaptured('button_clicked', {
button_id: 'signup',
location: 'header'
});