Package Exports
- @vigneshwaranbs/activity-tracker
- @vigneshwaranbs/activity-tracker/dist/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@vigneshwaranbs/activity-tracker) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
๐ฏ Activity Tracker
Universal user activity tracking library for web applications
Track user activity across your web applications with automatic inactivity detection, cross-tab synchronization, and real-time heartbeat monitoring.
โจ Features
Core Features
- ๐ฑ๏ธ Activity Detection - Tracks mouse, keyboard, scroll, and touch events
- โฑ๏ธ Inactivity Monitoring - Automatic detection after configurable timeout (default: 15 minutes)
- ๐ Heartbeat System - Regular server sync (default: 60 seconds)
- ๐ Cross-Tab Sync - Real-time activity sync across browser tabs via BroadcastChannel
- ๐ Cross-Domain Support - Server-based sync for different domains
- ๐ Page Visibility Tracking - Detects when users switch tabs (Page Visibility API)
- ๐ฏ Window Focus Tracking - Monitors when users switch to other apps/windows
- ๐ JWT Token Support - Automatic JWT decoding to extract userId
- ๐ Retry Logic - Automatic retry for failed heartbeats with exponential backoff
Detailed Behavior Tracking
- ๐ฑ๏ธ Click Tracking - Captures element ID, class, text, xpath, and coordinates
- ๐ Scroll Tracking - Records scroll depth %, position, and sections viewed
- ๐ Navigation Tracking - Monitors page views, time on page, and navigation path
- ๐๏ธ Viewport Tracking - Tracks visible elements and time in view
- ๐ฆ Event Batching - Automatically batches events (default: 30 seconds, max 100 events)
- ๐ Privacy-First - No PII, truncated text, respects data-no-track attribute
Developer Experience
- ๐ TypeScript Support - Full type definitions included
- โก Performance Optimized - Debounced events, passive listeners, minimal overhead
- ๐ง Highly Configurable - 30+ configuration options
- ๐ฑ Framework Agnostic - Works with React, Vue, Angular, Svelte, vanilla JS
- ๐ CDN Support - Drop-in script for no-build-tool projects
- ๐ Comprehensive Callbacks - 7 lifecycle callbacks for custom logic
- ๐จ HTML Attributes - Mark elements with data-section, data-track-viewport, data-no-track
๐ฆ Installation
Option 1: NPM Package (For Build Tools)
npm install @vigneshwaranbs/activity-trackeryarn add @vigneshwaranbs/activity-trackerpnpm add @vigneshwaranbs/activity-trackerOption 2: CDN (Drop-in Script - No Build Tools)
Basic Activity Tracking:
<!-- Latest version -->
<script src="https://unpkg.com/@vigneshwaranbs/activity-tracker@2.0.0/dist/activity-tracker-universal.js"></script>
<!-- Access globally as window.activityTracker -->
<script>
// Tracker auto-starts when page loads
console.log('Tracker:', window.activityTracker);
</script>Detailed Behavior Tracking:
<!-- Latest version with detailed tracking -->
<script src="https://unpkg.com/@vigneshwaranbs/activity-tracker@2.0.0/dist/activity-tracker-universal-phase2.js"></script>
<!-- Access globally as window.activityTracker -->
<script>
console.log('Detailed Tracker:', window.activityTracker);
</script>CDN Usage: Perfect for vanilla HTML, WordPress, legacy apps, or quick prototypes. No npm install needed!
๐ Quick Start
Two Tracking Methods Available
This library offers two tracking methods to suit different use cases:
- Basic Activity Tracking - Monitor user active/inactive status with heartbeat sync
- Detailed Behavior Tracking - Track clicks, scrolls, navigation, and viewport interactions
Method 1: Basic Activity Tracking
Perfect for session management, timeout detection, and presence monitoring.
NPM Installation:
npm install @vigneshwaranbs/activity-trackerBasic Usage:
import { ActivityTracker } from '@vigneshwaranbs/activity-tracker';
// Initialize tracker
const tracker = new ActivityTracker({
appId: 'my-app',
apiEndpoint: 'https://api.yourapp.com/api/user/activity',
getAuthToken: () => localStorage.getItem('userId') || ''
});
// Start tracking
tracker.start();
// Stop tracking (cleanup)
tracker.stop();React Example
import { useEffect } from 'react';
import { ActivityTracker } from '@vigneshwaranbs/activity-tracker';
function App() {
useEffect(() => {
const tracker = new ActivityTracker({
appId: 'my-react-app',
apiEndpoint: 'https://api.yourapp.com/api/user/activity',
getAuthToken: () => localStorage.getItem('userId') || '',
// Callbacks
onActive: (data) => {
console.log('โ
User is active', data);
},
onInactive: (data) => {
console.warn('โ ๏ธ User inactive for', data.inactivityDuration / 60000, 'minutes');
}
});
tracker.start();
return () => tracker.stop(); // Cleanup on unmount
}, []);
return <div>Your App</div>;
}Vue Example
<script setup>
import { onMounted, onUnmounted } from 'vue';
import { ActivityTracker } from '@vigneshwaranbs/activity-tracker';
let tracker;
onMounted(() => {
tracker = new ActivityTracker({
appId: 'my-vue-app',
apiEndpoint: 'https://api.yourapp.com/api/user/activity',
getAuthToken: () => localStorage.getItem('userId') || ''
});
tracker.start();
});
onUnmounted(() => {
tracker?.stop();
});
</script>CDN Usage (No Build Tools):
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
</head>
<body>
<h1>My Application</h1>
<!-- Step 1: Set JWT token in localStorage (your login code) -->
<script>
// After successful login, store JWT token
localStorage.setItem('authToken', 'eyJhbGci...');
</script>
<!-- Step 2: Include universal script - appId auto-generated from domain -->
<script src="https://unpkg.com/@vigneshwaranbs/activity-tracker@2.0.3/dist/activity-tracker-universal.js"
data-api-endpoint="https://api.yourapp.com/api/user/activity">
</script>
<!-- That's it! Basic tracking starts automatically -->
<!-- appId is automatically generated from domain (e.g., www.app.xxxx.com โ app.xxxx) -->
</body>
</html>Auto-Generated AppId:
The tracker automatically generates appId from your domain name:
| Domain | Auto-Generated AppId |
|---|---|
www.app.xxxx.com |
app.xxxx |
app.xxxx.com |
app.xxxx |
xxxx.com |
xxxx |
localhost |
localhost |
Override AppId (Optional):
<!-- Manually specify appId if you want a custom name -->
<script src="https://unpkg.com/@vigneshwaranbs/activity-tracker@2.0.3/dist/activity-tracker-universal.js"
data-app-id="custom-app-name"
data-api-endpoint="https://api.yourapp.com/api/user/activity">
</script>Basic Tracking Configuration (CDN):
<!-- All configuration via data attributes -->
<script src="https://unpkg.com/@vigneshwaranbs/activity-tracker@2.0.0/dist/activity-tracker-universal.js"
data-app-id="my-app"
data-api-endpoint="https://api.yourapp.com/api/user/activity"
data-auth-token-key="authToken"
data-inactivity-timeout="900000"
data-heartbeat-interval="60000"
data-log-level="info">
</script>Detailed Tracking Configuration (CDN):
<!-- Configuration for detailed tracking -->
<script src="https://unpkg.com/@vigneshwaranbs/activity-tracker@2.0.0/dist/activity-tracker-universal-phase2.js"
data-app-id="my-app"
data-api-endpoint="https://api.yourapp.com/api/user/activity"
data-events-api-endpoint="https://api.yourapp.com/api/user/activity/events"
data-auth-token-key="authToken"
data-track-clicks="true"
data-track-scrolls="true"
data-track-navigation="true"
data-track-viewport="true"
data-event-batch-interval="30000"
data-max-events-per-batch="100">
</script>Or use global config object:
<script>
window.ACTIVITY_TRACKER_CONFIG = {
appId: 'my-app',
apiEndpoint: 'https://api.yourapp.com/api/user/activity',
authTokenKey: 'authToken',
inactivityTimeout: 900000,
heartbeatInterval: 60000,
// For detailed tracking (phase2 script only)
eventsApiEndpoint: 'https://api.yourapp.com/api/user/activity/events',
trackClicks: true,
trackScrolls: true,
trackNavigation: true,
trackViewport: true,
// Callbacks
onActive: function(data) {
console.log('User is active!', data);
},
onInactive: function(data) {
console.warn('User is inactive!', data);
}
};
</script>
<!-- Choose appropriate script -->
<script src="https://unpkg.com/@vigneshwaranbs/activity-tracker@2.0.3/dist/activity-tracker-universal-phase2.js"></script>CDN is perfect for:
- โ Vanilla HTML/CSS/JS projects
- โ WordPress, Shopify, Wix sites
- โ Legacy applications without build tools
- โ Quick prototypes and demos
- โ No npm install or build step needed
- โ Auto-generated appId from domain (no manual configuration needed)
Method 2: Detailed Behavior Tracking
Perfect for analytics, UX optimization, heatmaps, and understanding user behavior.
Features:
- ๐ฑ๏ธ Click Tracking - Captures element ID, class, text, xpath, and coordinates
- ๐ Scroll Tracking - Records scroll depth %, position, and sections viewed
- ๐ Navigation Tracking - Monitors page views, time on page, and navigation path
- ๐๏ธ Viewport Tracking - Tracks visible elements and time in view
- ๐ฆ Event Batching - Automatically batches events (sends every 30 seconds)
- ๐ Privacy-First - No PII, truncated text, respects data-no-track attribute
- โก Performance Optimized - Debounced events, passive listeners, minimal overhead
NPM Installation:
npm install @vigneshwaranbs/activity-trackerUsage:
import { DetailedActivityTracker } from '@vigneshwaranbs/activity-tracker';
const tracker = new DetailedActivityTracker({
// Phase 1 config (required)
appId: 'my-app',
apiEndpoint: 'https://api.yourapp.com/api/user/activity',
authToken: localStorage.getItem('authToken'),
// Phase 2 config (new)
trackClicks: true,
trackScrolls: true,
trackNavigation: true,
trackViewport: true,
eventsApiEndpoint: 'https://api.yourapp.com/api/user/activity/events',
eventBatchInterval: 30000, // 30 seconds
maxEventsPerBatch: 100,
// Phase 2 callbacks
onEventTracked: (event) => {
console.log('Event tracked:', event);
},
onEventBatchSent: (response) => {
console.log('Batch sent:', response.eventsReceived, 'events');
}
});
tracker.start();CDN Usage (No Build Tools):
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
</head>
<body>
<h1>My Application</h1>
<!-- Step 1: Set JWT token in localStorage -->
<script>
localStorage.setItem('authToken', 'eyJhbGci...');
</script>
<!-- Step 2: Include universal script - detailed tracking with auto-generated appId -->
<script src="https://unpkg.com/@vigneshwaranbs/activity-tracker@2.0.3/dist/activity-tracker-universal-phase2.js"
data-api-endpoint="https://api.yourapp.com/api/user/activity"
data-events-api-endpoint="https://api.yourapp.com/api/user/activity/events"
data-track-clicks="true"
data-track-scrolls="true"
data-track-navigation="true"
data-track-viewport="true">
</script>
<!-- Detailed tracking starts automatically -->
<!-- appId is auto-generated from domain (e.g., www.app.xxxx.com โ app.xxxx) -->
<!-- To override: add data-app-id="custom-name" -->
</body>
</html>๐ Event Types
1. Click Events
{
eventId: "evt-1703456789-abc123",
eventType: "click",
timestamp: 1703456789000,
url: "/products",
element: "button",
elementId: "buy-now-btn",
elementClass: "btn btn-primary",
text: "Buy Now",
xpath: "/html/body/div[1]/button",
x: 350,
y: 250
}2. Scroll Events
{
eventId: "evt-1703456790-def456",
eventType: "scroll",
timestamp: 1703456790000,
url: "/products",
scrollDepth: 75, // 75% down page
scrollPosition: 1200,
pageHeight: 1600,
viewportHeight: 800,
sectionsViewed: ["hero", "features", "pricing"]
}3. Navigation Events
{
eventId: "evt-1703456791-ghi789",
eventType: "navigation",
timestamp: 1703456791000,
url: "/checkout",
fromUrl: "/products",
toUrl: "/checkout",
timeOnPage: 45000 // 45 seconds on previous page
}4. Viewport Events
{
eventId: "evt-1703456792-jkl012",
eventType: "viewport",
timestamp: 1703456792000,
url: "/products",
elementsInView: [
{
id: "product-card-1",
class: "product-card",
tag: "div",
text: "Premium Product",
timeInView: 5000 // 5 seconds
}
]
}๐จ HTML Attributes for Tracking
Track Scroll Sections
<div data-section="hero">
<!-- Hero content -->
</div>
<div data-section="features">
<!-- Features content -->
</div>
<div data-section="pricing">
<!-- Pricing content -->
</div>When users scroll through these sections, they'll be recorded in sectionsViewed array.
Track Viewport Elements
<div data-track-viewport id="important-content">
<!-- This element's visibility will be tracked -->
</div>
<div data-track-viewport class="product-card">
<!-- Track how long users view this product -->
</div>Elements with data-track-viewport attribute are monitored for visibility and time in view.
Exclude Elements from Tracking
<button data-no-track="true">
<!-- This button won't be tracked -->
</button>
<div data-no-track="true">
<!-- Clicks inside this div won't be tracked -->
</div>๐ง Detailed Tracking Configuration Options
{
// Feature Flags
trackClicks?: boolean; // Enable click tracking (default: true)
trackScrolls?: boolean; // Enable scroll tracking (default: true)
trackNavigation?: boolean; // Enable navigation tracking (default: true)
trackViewport?: boolean; // Enable viewport tracking (default: true)
// API Endpoints
eventsApiEndpoint?: string; // Separate endpoint for events
// Default: apiEndpoint + '/events'
// Batching Settings
eventBatchInterval?: number; // How often to send events (ms)
// Default: 30000 (30 seconds)
maxEventsPerBatch?: number; // Max events per batch
// Default: 100
// Callbacks
onEventTracked?: (event: ActivityEvent) => void;
onEventBatchSent?: (response: EventBatchResponse) => void;
onEventBatchError?: (error: Error) => void;
}๐ฑ Framework Examples - Detailed Tracking
React with Detailed Tracking
import { useEffect } from 'react';
import { DetailedActivityTracker } from '@vigneshwaranbs/activity-tracker';
function App() {
useEffect(() => {
const tracker = new DetailedActivityTracker({
appId: 'my-react-app',
apiEndpoint: 'https://api.yourapp.com/api/user/activity',
eventsApiEndpoint: 'https://api.yourapp.com/api/user/activity/events',
authToken: localStorage.getItem('authToken'),
// Enable all Phase 2 features
trackClicks: true,
trackScrolls: true,
trackNavigation: true,
trackViewport: true,
// Callbacks
onEventTracked: (event) => {
// Send to analytics
if (event.eventType === 'click') {
analytics.track('Button Clicked', {
elementId: event.elementId,
text: event.text
});
}
}
});
tracker.start();
return () => tracker.stop();
}, []);
return (
<div>
<div data-section="hero">
<h1>Welcome</h1>
<button id="cta-button">Get Started</button>
</div>
<div data-section="features" data-track-viewport>
<h2>Features</h2>
{/* Feature cards */}
</div>
</div>
);
}Vue with Detailed Tracking
<template>
<div>
<div data-section="hero">
<h1>Welcome</h1>
<button id="cta-button">Get Started</button>
</div>
<div data-section="features" data-track-viewport>
<h2>Features</h2>
</div>
</div>
</template>
<script setup>
import { onMounted, onUnmounted } from 'vue';
import { DetailedActivityTracker } from '@vigneshwaranbs/activity-tracker';
let tracker;
onMounted(() => {
tracker = new DetailedActivityTracker({
appId: 'my-vue-app',
apiEndpoint: 'https://api.yourapp.com/api/user/activity',
eventsApiEndpoint: 'https://api.yourapp.com/api/user/activity/events',
authToken: localStorage.getItem('authToken'),
trackClicks: true,
trackScrolls: true,
trackNavigation: true,
trackViewport: true
});
tracker.start();
});
onUnmounted(() => {
tracker?.stop();
});
</script>๐ Use Cases - Detailed Tracking
1. E-commerce Analytics
Track which products users view, how long they spend on product pages, and which "Add to Cart" buttons they click:
const tracker = new DetailedActivityTracker({
appId: 'ecommerce-store',
trackClicks: true, // Track "Add to Cart", "Buy Now" clicks
trackScrolls: true, // See how far users scroll on product pages
trackViewport: true, // Track which products are viewed
onEventTracked: (event) => {
if (event.eventType === 'click' && event.elementId?.includes('add-to-cart')) {
// User clicked "Add to Cart"
sendToAnalytics('add_to_cart', { productId: extractProductId(event.url) });
}
if (event.eventType === 'viewport' && event.elementsInView.length > 0) {
// Track product impressions
event.elementsInView.forEach(el => {
if (el.class?.includes('product-card')) {
sendToAnalytics('product_view', { productId: el.id });
}
});
}
}
});2. Content Engagement
Measure how users engage with blog posts or documentation:
const tracker = new DetailedActivityTracker({
appId: 'blog',
trackScrolls: true, // Measure read depth
trackNavigation: true, // Time spent on each article
onEventTracked: (event) => {
if (event.eventType === 'scroll') {
// User scrolled to 75% - likely read the article
if (event.scrollDepth >= 75) {
sendToAnalytics('article_read', {
url: event.url,
scrollDepth: event.scrollDepth
});
}
}
if (event.eventType === 'navigation') {
// Track time spent on article
if (event.timeOnPage > 60000) { // More than 1 minute
sendToAnalytics('article_engagement', {
url: event.fromUrl,
timeSpent: event.timeOnPage
});
}
}
}
});3. Form Optimization
Track which form fields users interact with:
<form>
<input type="text" id="email" placeholder="Email">
<input type="text" id="phone" placeholder="Phone">
<button id="submit-form">Submit</button>
</form>
<script>
const tracker = new DetailedActivityTracker({
appId: 'signup-form',
trackClicks: true,
onEventTracked: (event) => {
if (event.eventType === 'click') {
if (event.elementId === 'submit-form') {
// Track form submission
sendToAnalytics('form_submit');
}
}
}
});
</script>๐ Privacy & Security
Detailed tracking respects user privacy:
- โ No PII Captured - Never tracks password inputs, credit card fields
- โ Text Truncation - Element text limited to 100 characters
- โ
Opt-out Support -
data-no-trackattribute excludes elements - โ No Form Values - Only tracks field interactions, not values
- โ GDPR Compliant - Respects user consent and data retention policies
Excluded by Default:
<input type="password"><input type="hidden">- Elements with
data-no-track="true"
๐ Backend Integration - Detailed Tracking
Detailed tracking uses two API endpoints:
Basic Tracking Endpoint (Heartbeat):
POST /api/user/activityDetailed Tracking Endpoint (Events):
POST /api/user/activity/eventsExample Backend (Express + MongoDB):
// Event batching endpoint for detailed tracking
app.post('/api/user/activity/events', async (req, res) => {
const { userId, appId, events } = req.body;
// Validate
if (!userId || !appId || !events || events.length === 0) {
return res.status(400).json({ success: false, message: 'Invalid request' });
}
if (events.length > 100) {
return res.status(400).json({ success: false, message: 'Max 100 events per batch' });
}
// Append events to user document
await db.collection('user_sessions').updateOne(
{ userId: new ObjectId(userId), appId },
{
$push: {
events: {
$each: events,
$slice: -1000 // Keep only last 1000 events
}
},
$set: { updatedAt: new Date() }
},
{ upsert: true }
);
res.json({
success: true,
eventsReceived: events.length
});
});MongoDB Document Structure:
{
_id: ObjectId("..."),
userId: ObjectId("..."),
appId: "my-app",
// Basic tracking fields
isActive: true,
lastActivityTime: ISODate("2024-12-24T10:30:00Z"),
lastHeartbeat: ISODate("2024-12-24T10:30:00Z"),
tabVisible: true,
windowFocused: true,
// Detailed tracking field (only if using DetailedActivityTracker)
events: [
{
eventType: "click",
element: "button",
timestamp: ISODate("2024-12-24T10:30:00Z"),
...
},
{
eventType: "scroll",
scrollDepth: 75,
timestamp: ISODate("2024-12-24T10:31:00Z"),
...
},
{
eventType: "navigation",
fromUrl: "/home",
toUrl: "/about",
timestamp: ISODate("2024-12-24T10:32:00Z"),
...
}
],
createdAt: ISODate("2024-12-24T09:00:00Z"),
updatedAt: ISODate("2024-12-24T10:30:00Z")
}๐๏ธ Advanced Usage - Detailed Tracking
Selective Feature Enabling
// Only track clicks and scrolls
const tracker = new DetailedActivityTracker({
appId: 'my-app',
trackClicks: true,
trackScrolls: true,
trackNavigation: false, // Disabled
trackViewport: false // Disabled
});Custom Event Handling
const tracker = new DetailedActivityTracker({
appId: 'my-app',
trackClicks: true,
onEventTracked: (event) => {
// Send to multiple analytics platforms
if (event.eventType === 'click') {
googleAnalytics.event('click', { elementId: event.elementId });
mixpanel.track('Button Click', { button: event.text });
amplitude.logEvent('click', { element: event.element });
}
},
onEventBatchSent: (response) => {
console.log(`โ
Sent ${response.eventsReceived} events to backend`);
},
onEventBatchError: (error) => {
console.error('โ Failed to send events:', error);
// Retry logic or alert user
}
});Force Send Batch
// Manually trigger batch send (useful before page unload)
tracker.forceSendBatch();
// Example: Send batch before user leaves
window.addEventListener('beforeunload', () => {
tracker.forceSendBatch();
});Get Current Batch Size
const batchSize = tracker.getBatchSize();
console.log(`Current batch has ${batchSize} events`);๐ Performance Considerations
Detailed tracking is optimized for production:
- โ Debounced Events - Scroll events debounced by 1 second
- โ Passive Listeners - Non-blocking event listeners
- โ Event Batching - Reduces API calls by 50%
- โ Smart Filtering - Only tracks significant scroll changes (5%+)
- โ Memory Management - Automatic queue cleanup
- โ Minimal Overhead - <1% CPU usage, <50MB memory
Data Volume Estimate (1000 users):
- Events per user per day: ~5,000
- Storage per user per day: ~25 MB
- With 1000-event limit: ~5 MB per user
- Total: ~5 GB per day (1000 users)
Optimization Tips:
- Use 90-day data retention (auto-delete old events)
- Enable only needed features (disable viewport if not needed)
- Increase
eventBatchIntervalto reduce API calls - Use sampling (track 10% of users for detailed analytics)
๐ Comparison: Basic vs Detailed Tracking
| Feature | Basic Tracking | Detailed Tracking |
|---|---|---|
| Active/Inactive Detection | โ | โ |
| Heartbeat to Backend | โ | โ |
| Tab Visibility | โ | โ |
| Window Focus | โ | โ |
| Click Tracking | โ | โ |
| Scroll Tracking | โ | โ |
| Navigation Tracking | โ | โ |
| Viewport Tracking | โ | โ |
| Event Batching | โ | โ |
| Class | ActivityTracker |
DetailedActivityTracker |
| CDN Script | activity-tracker-universal.js |
activity-tracker-universal-phase2.js |
| API Endpoints | 1 endpoint | 2 endpoints |
| Use Case | Session management, timeouts | Analytics, UX optimization |
Choose Basic Tracking for:
- โ Session timeout management
- โ User presence monitoring
- โ Active/inactive status
- โ Lightweight tracking
- โ Simple analytics
Choose Detailed Tracking for:
- โ E-commerce analytics
- โ Content engagement metrics
- โ UX optimization
- โ A/B testing insights
- โ Heatmap data collection
- โ User behavior analysis
โ๏ธ Configuration Reference
Basic Tracking Configuration
Required Options
| Option | Type | Description |
|---|---|---|
appId |
string |
Unique identifier for your application |
apiEndpoint |
string |
API endpoint URL for heartbeat requests |
userId or authToken or getAuthToken |
string | function |
User identification |
Optional Options
| Option | Type | Default | Description |
|---|---|---|---|
inactivityTimeout |
number |
900000 (15 min) |
Time before marking user inactive (ms) |
heartbeatInterval |
number |
60000 (1 min) |
Interval between heartbeat requests (ms) |
checkInterval |
number |
10000 (10 sec) |
Interval for checking inactivity (ms) |
crossTabSync |
boolean |
true |
Enable cross-tab activity sync via BroadcastChannel |
serverSync |
boolean |
true |
Enable server heartbeat sync |
trackPageVisibility |
boolean |
true |
Track tab visibility changes (Page Visibility API) |
trackWindowFocus |
boolean |
true |
Track window focus/blur events |
debounceDelay |
number |
1000 (1 sec) |
Debounce delay for activity events (ms) |
retryAttempts |
number |
3 |
Number of retry attempts for failed heartbeats |
logLevel |
string |
'warn' |
Log level: 'debug', 'info', 'warn', 'error', 'none' |
customHeaders |
object |
{} |
Custom HTTP headers for API requests |
metadata |
object |
{} |
Custom metadata to include in heartbeat |
CDN Data Attributes (Basic Tracking)
| Attribute | Type | Default | Description |
|---|---|---|---|
data-app-id |
string |
required | Application identifier |
data-api-endpoint |
string |
required | Heartbeat API endpoint |
data-auth-token-key |
string |
'authToken' |
localStorage key for auth token |
data-user-id-key |
string |
'userId' |
localStorage key for user ID |
data-inactivity-timeout |
number |
900000 |
Inactivity timeout in milliseconds |
data-heartbeat-interval |
number |
60000 |
Heartbeat interval in milliseconds |
data-check-interval |
number |
10000 |
Activity check interval in milliseconds |
data-cross-tab-sync |
boolean |
true |
Enable cross-tab sync |
data-server-sync |
boolean |
true |
Enable server sync |
data-track-page-visibility |
boolean |
true |
Track page visibility |
data-track-window-focus |
boolean |
true |
Track window focus |
data-debounce-delay |
number |
1000 |
Debounce delay in milliseconds |
data-retry-attempts |
number |
3 |
Retry attempts for failed requests |
data-log-level |
string |
'warn' |
Log level |
Callbacks
| Callback | Parameters | Description |
|---|---|---|
onActive |
(data: ActivityData) => void |
Called when user becomes active |
onInactive |
(data: InactivityData) => void |
Called when user becomes inactive |
onHeartbeatSuccess |
(response: HeartbeatResponse) => void |
Called on successful heartbeat |
onHeartbeatError |
(error: Error) => void |
Called on heartbeat failure |
Detailed Tracking Configuration
Additional Options (extends Basic Tracking)
| Option | Type | Default | Description |
|---|---|---|---|
eventsApiEndpoint |
string |
apiEndpoint + '/events' |
Separate endpoint for event batching |
trackClicks |
boolean |
true |
Enable click event tracking |
trackScrolls |
boolean |
true |
Enable scroll event tracking |
trackNavigation |
boolean |
true |
Enable navigation event tracking |
trackViewport |
boolean |
true |
Enable viewport element tracking |
eventBatchInterval |
number |
30000 (30 sec) |
How often to send event batches (ms) |
maxEventsPerBatch |
number |
100 |
Maximum events per batch |
CDN Data Attributes (Detailed Tracking)
| Attribute | Type | Default | Description |
|---|---|---|---|
data-events-api-endpoint |
string |
apiEndpoint + '/events' |
Events API endpoint |
data-track-clicks |
boolean |
true |
Enable click tracking |
data-track-scrolls |
boolean |
true |
Enable scroll tracking |
data-track-navigation |
boolean |
true |
Enable navigation tracking |
data-track-viewport |
boolean |
true |
Enable viewport tracking |
data-event-batch-interval |
number |
30000 |
Event batch interval in milliseconds |
data-max-events-per-batch |
number |
100 |
Max events per batch |
Additional Callbacks
| Callback | Parameters | Description |
|---|---|---|
onEventTracked |
(event: ActivityEvent) => void |
Called when an event is tracked |
onEventBatchSent |
(response: EventBatchResponse) => void |
Called when batch is sent successfully |
onEventBatchError |
(error: Error) => void |
Called when batch send fails |
Configuration Examples
Minimal Configuration (NPM)
const tracker = new ActivityTracker({
appId: 'my-app',
apiEndpoint: 'https://api.yourapp.com/api/user/activity',
userId: 'user-123'
});
tracker.start();Minimal Configuration (CDN)
<script src="https://unpkg.com/@vigneshwaranbs/activity-tracker@2.0.0/dist/activity-tracker-universal.js"
data-app-id="my-app"
data-api-endpoint="https://api.yourapp.com/api/user/activity">
</script>Full Configuration (NPM)
const tracker = new DetailedActivityTracker({
// Basic tracking
appId: 'my-app',
apiEndpoint: 'https://api.yourapp.com/api/user/activity',
getAuthToken: () => localStorage.getItem('authToken') || '',
inactivityTimeout: 600000, // 10 minutes
heartbeatInterval: 30000, // 30 seconds
checkInterval: 5000, // 5 seconds
crossTabSync: true,
serverSync: true,
trackPageVisibility: true,
trackWindowFocused: true,
debounceDelay: 2000, // 2 seconds
retryAttempts: 5,
logLevel: 'info',
// Detailed tracking
eventsApiEndpoint: 'https://api.yourapp.com/api/user/activity/events',
trackClicks: true,
trackScrolls: true,
trackNavigation: true,
trackViewport: true,
eventBatchInterval: 20000, // 20 seconds
maxEventsPerBatch: 50,
// Callbacks
onActive: (data) => console.log('Active:', data),
onInactive: (data) => console.warn('Inactive:', data),
onHeartbeatSuccess: (res) => console.log('Heartbeat:', res),
onHeartbeatError: (err) => console.error('Error:', err),
onEventTracked: (event) => console.log('Event:', event),
onEventBatchSent: (res) => console.log('Batch sent:', res),
onEventBatchError: (err) => console.error('Batch error:', err)
});
tracker.start();Full Configuration (CDN)
<script src="https://unpkg.com/@vigneshwaranbs/activity-tracker@2.0.0/dist/activity-tracker-universal-phase2.js"
data-app-id="my-app"
data-api-endpoint="https://api.yourapp.com/api/user/activity"
data-events-api-endpoint="https://api.yourapp.com/api/user/activity/events"
data-auth-token-key="authToken"
data-inactivity-timeout="600000"
data-heartbeat-interval="30000"
data-check-interval="5000"
data-cross-tab-sync="true"
data-server-sync="true"
data-track-page-visibility="true"
data-track-window-focus="true"
data-debounce-delay="2000"
data-retry-attempts="5"
data-log-level="info"
data-track-clicks="true"
data-track-scrolls="true"
data-track-navigation="true"
data-track-viewport="true"
data-event-batch-interval="20000"
data-max-events-per-batch="50">
</script>๐ Advanced Examples
With Custom Metadata
const tracker = new ActivityTracker({
appId: 'my-app',
apiEndpoint: 'https://api.yourapp.com/api/user/activity',
getAuthToken: () => localStorage.getItem('userId') || '',
// Custom metadata
metadata: {
userAgent: navigator.userAgent,
screenResolution: `${window.screen.width}x${window.screen.height}`,
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
language: navigator.language,
referrer: document.referrer
}
});
tracker.start();With Custom Headers (Authentication)
const tracker = new ActivityTracker({
appId: 'my-app',
apiEndpoint: 'https://api.yourapp.com/api/user/activity',
userId: 'user-123',
// Custom headers for API authentication
customHeaders: {
'Authorization': `Bearer ${localStorage.getItem('authToken')}`,
'X-API-Key': 'your-api-key',
'X-App-Version': '1.0.0'
}
});
tracker.start();With All Callbacks
const tracker = new ActivityTracker({
appId: 'my-app',
apiEndpoint: 'https://api.yourapp.com/api/user/activity',
getAuthToken: () => localStorage.getItem('userId') || '',
// Activity callbacks
onActive: (data) => {
console.log('โ
User is active');
console.log('Last activity:', new Date(data.lastActivityTime));
// Send to analytics
analytics.track('user_active', data);
},
onInactive: (data) => {
console.warn('โ ๏ธ User inactive');
console.warn('Duration:', data.inactivityDuration / 60000, 'minutes');
// Show warning modal
showInactivityWarning();
},
onHeartbeatSuccess: (response) => {
console.log('๐ Heartbeat sent successfully');
// Handle server response
if (response.action === 'logout') {
logout();
}
},
onHeartbeatError: (error) => {
console.error('โ Heartbeat failed:', error.message);
// Send to error tracking (e.g., Sentry)
Sentry.captureException(error);
}
});
tracker.start();Development vs Production Config
const isDevelopment = process.env.NODE_ENV === 'development';
const tracker = new ActivityTracker({
appId: isDevelopment ? 'my-app-dev' : 'my-app',
apiEndpoint: isDevelopment
? 'http://localhost:4000/api/user/activity'
: 'https://api.yourapp.com/api/user/activity',
getAuthToken: () => localStorage.getItem('userId') || '',
// Development: shorter intervals for testing
inactivityTimeout: isDevelopment ? 2 * 60 * 1000 : 15 * 60 * 1000,
heartbeatInterval: isDevelopment ? 10 * 1000 : 60 * 1000,
// Development: verbose logging
logLevel: isDevelopment ? 'debug' : 'warn'
});
tracker.start();React Custom Hook
// hooks/useActivityTracker.ts
import { useEffect, useState } from 'react';
import { ActivityTracker } from '@vigneshwaranbs/activity-tracker';
export function useActivityTracker(appId: string) {
const [isActive, setIsActive] = useState(true);
const [tracker, setTracker] = useState<ActivityTracker | null>(null);
useEffect(() => {
const activityTracker = new ActivityTracker({
appId,
apiEndpoint: 'https://api.yourapp.com/api/user/activity',
getAuthToken: () => localStorage.getItem('userId') || '',
onActive: () => setIsActive(true),
onInactive: () => setIsActive(false)
});
activityTracker.start();
setTracker(activityTracker);
return () => activityTracker.stop();
}, [appId]);
return { isActive, tracker };
}
// Usage
function MyComponent() {
const { isActive } = useActivityTracker('my-app');
return (
<div>
<StatusBadge active={isActive} />
{!isActive && <InactivityWarning />}
</div>
);
}๐ Backend API Integration
Expected API Endpoint
Your backend should handle POST requests to the configured apiEndpoint:
Request:
POST /api/user/activity
Content-Type: application/json
{
"userId": "676966c6c0b4b40f8cc2db9a",
"appId": "my-app",
"isActive": true,
"lastActivityTime": 1703456789000,
"tabVisible": true,
"windowFocused": true,
"metadata": {
"userAgent": "Mozilla/5.0...",
"platform": "MacIntel",
"language": "en-US",
"screenResolution": "1920x1080",
"viewport": "1440x900",
"timezone": "America/New_York",
"url": "https://app.example.com/dashboard"
}
}Response:
{
"success": true,
"message": "Activity tracked successfully",
"activeInOtherApp": false,
"lastActivityTime": 1703456789000,
"action": "continue" // or "logout", "warn"
}Express.js Backend Example
// server.js
import express from 'express';
import { MongoClient, ObjectId } from 'mongodb';
const app = express();
app.use(express.json());
const MONGODB_URI = process.env.MONGODB_URI;
const client = await MongoClient.connect(MONGODB_URI);
const db = client.db('your-database');
app.post('/api/user/activity', async (req, res) => {
try {
const { userId, appId, isActive, lastActivityTime, tabVisible, windowFocused, metadata } = req.body;
const now = new Date();
const sessionData = {
userId: new ObjectId(userId),
appId,
isActive,
lastActivityTime: new Date(lastActivityTime),
lastHeartbeat: now,
tabVisible,
windowFocused,
metadata,
updatedAt: now
};
// Upsert: one document per userId + appId
await db.collection('user_sessions').updateOne(
{ userId: new ObjectId(userId), appId },
{ $set: sessionData, $setOnInsert: { createdAt: now } },
{ upsert: true }
);
res.json({ success: true, message: 'Activity tracked successfully' });
} catch (error) {
console.error('Activity tracking error:', error);
res.status(500).json({ success: false, error: error.message });
}
});
app.listen(4000, () => console.log('Server running on port 4000'));MongoDB Schema
// Collection: user_sessions
{
"_id": ObjectId("..."),
"userId": ObjectId("676966c6c0b4b40f8cc2db9a"),
"appId": "my-app",
"isActive": true,
"lastActivityTime": ISODate("2024-12-24T10:30:00.000Z"),
"lastHeartbeat": ISODate("2024-12-24T10:30:00.000Z"),
"tabVisible": true,
"windowFocused": true,
"metadata": {
"userAgent": "Mozilla/5.0...",
"platform": "MacIntel",
"language": "en-US",
"screenResolution": "1920x1080",
"viewport": "1440x900",
"timezone": "America/New_York",
"url": "https://app.example.com/dashboard"
},
"createdAt": ISODate("2024-12-24T09:00:00.000Z"),
"updatedAt": ISODate("2024-12-24T10:30:00.000Z")
}
// Recommended indexes
db.user_sessions.createIndex({ userId: 1, appId: 1 }, { unique: true });
db.user_sessions.createIndex({ lastActivityTime: -1 });
db.user_sessions.createIndex({ isActive: 1 });
db.user_sessions.createIndex({ updatedAt: -1 });๐ฏ Use Cases
1. Session Timeout Management
const tracker = new ActivityTracker({
appId: 'my-app',
apiEndpoint: 'https://api.yourapp.com/api/user/activity',
getAuthToken: () => localStorage.getItem('userId') || '',
inactivityTimeout: 15 * 60 * 1000, // 15 minutes
onInactive: (data) => {
// Show warning modal
showModal({
title: 'Session Timeout Warning',
message: `You've been inactive for ${data.inactivityDuration / 60000} minutes. Your session will expire soon.`,
actions: ['Stay Logged In', 'Logout']
});
}
});2. Multi-Tab Activity Sync
const tracker = new ActivityTracker({
appId: 'my-app',
apiEndpoint: 'https://api.yourapp.com/api/user/activity',
getAuthToken: () => localStorage.getItem('userId') || '',
crossTabSync: true, // Enable cross-tab sync
onActive: () => {
// Activity detected in any tab syncs to all tabs
console.log('User active in this or another tab');
}
});3. Analytics Integration
const tracker = new ActivityTracker({
appId: 'my-app',
apiEndpoint: 'https://api.yourapp.com/api/user/activity',
getAuthToken: () => localStorage.getItem('userId') || '',
onActive: (data) => {
// Track active sessions in analytics
analytics.track('session_active', {
userId: data.userId,
appId: data.appId,
timestamp: data.lastActivityTime
});
},
onInactive: (data) => {
// Track session end in analytics
analytics.track('session_inactive', {
userId: data.userId,
duration: data.inactivityDuration
});
}
});4. Real-Time User Presence
const tracker = new ActivityTracker({
appId: 'collaboration-app',
apiEndpoint: 'https://api.yourapp.com/api/user/activity',
getAuthToken: () => localStorage.getItem('userId') || '',
heartbeatInterval: 30 * 1000, // 30 seconds for real-time presence
onHeartbeatSuccess: (response) => {
// Update UI with active users from server response
updateActiveUsersList(response.activeUsers);
}
});๐ What Gets Tracked?
Activity Events
- Mouse:
mousedown,mousemove,click - Keyboard:
keypress,keydown - Touch:
touchstart(mobile devices) - Scroll:
scroll(page scrolling)
Browser State
- Page Visibility: Tab active/hidden (Visibility API)
- Window Focus: Window focused/blurred
- Tab Visibility: User switched tabs
Metadata (Optional)
- User agent
- Platform (OS)
- Language
- Screen resolution
- Viewport size
- Timezone
- Current URL
- Referrer
- Custom metadata
๐๏ธ Architecture
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Browser Tab 1 โ
โ ActivityTracker Instance โ
โ โโ DOM Event Listeners (mouse, keyboard, scroll) โ
โ โโ BroadcastChannel (cross-tab sync) โ
โ โโ Heartbeat Timer (60s) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โ POST /api/user/activity
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Backend API โ
โ Express / Next.js / Fastify โ
โ โโ POST /api/user/activity โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โ Upsert (one doc per user)
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ MongoDB Database โ
โ Collection: user_sessions โ
โ โโ { userId, appId, isActive, lastActivityTime } โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโก Performance
Optimizations
- Debouncing: Activity events debounced by 1 second (configurable)
- Passive Listeners: Non-blocking event listeners for scroll/touch
- Efficient Intervals: Minimal timer overhead
- Retry Queue: Failed heartbeats queued and retried
- Cross-Tab Sync: BroadcastChannel for instant sync (no polling)
Benchmarks
- Memory: ~50KB (minified + gzipped)
- CPU: < 0.1% average
- Network: 1 request per 60 seconds (configurable)
- Event Overhead: < 1ms per debounced event
๐ ๏ธ TypeScript Support
Full TypeScript definitions included:
import {
ActivityTracker,
ActivityTrackerConfig,
ActivityData,
InactivityData,
HeartbeatPayload,
HeartbeatResponse
} from '@vigneshwaranbs/activity-tracker';
const config: ActivityTrackerConfig = {
appId: 'my-app',
apiEndpoint: 'https://api.yourapp.com/api/user/activity',
getAuthToken: () => localStorage.getItem('userId') || '',
inactivityTimeout: 15 * 60 * 1000,
onActive: (data: ActivityData) => console.log(data),
onInactive: (data: InactivityData) => console.warn(data)
};
const tracker = new ActivityTracker(config);
tracker.start();๐งช Testing
Manual Testing
// Create tracker with short timeouts for testing
const tracker = new ActivityTracker({
appId: 'test-app',
apiEndpoint: 'http://localhost:4000/api/user/activity',
userId: 'test-user',
// Short intervals for testing
inactivityTimeout: 30 * 1000, // 30 seconds
heartbeatInterval: 10 * 1000, // 10 seconds
checkInterval: 5 * 1000, // 5 seconds
// Verbose logging
logLevel: 'debug',
// Test callbacks
onActive: () => console.log('โ
ACTIVE'),
onInactive: () => console.log('โ ๏ธ INACTIVE'),
onHeartbeatSuccess: (res) => console.log('๐ HEARTBEAT', res),
onHeartbeatError: (err) => console.error('โ ERROR', err)
});
tracker.start();
// Test inactivity: Stop moving mouse for 30 seconds
// Test heartbeat: Check network tab for POST requests every 10 seconds
// Test cross-tab: Open multiple tabs and check sync๐ API Reference
Constructor
new ActivityTracker(config: ActivityTrackerConfig)Methods
| Method | Description |
|---|---|
start() |
Start tracking user activity |
stop() |
Stop tracking and cleanup |
isUserActive() |
Check if user is currently active |
getLastActivityTime() |
Get timestamp of last activity |
forceHeartbeat() |
Manually trigger a heartbeat |
Example
const tracker = new ActivityTracker({ /* config */ });
tracker.start(); // Start tracking
const active = tracker.isUserActive(); // Check status
const lastTime = tracker.getLastActivityTime(); // Get timestamp
tracker.forceHeartbeat(); // Force sync
tracker.stop(); // Stop and cleanup๐ค Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
๐ License
MIT ยฉ Vigneshwaran BS
๐ Links
- NPM Package: https://www.npmjs.com/package/@vigneshwaranbs/activity-tracker
- GitHub Repository: https://github.com/vigneshwaranbs/activity-tracker
- Issues: https://github.com/vigneshwaranbs/activity-tracker/issues
- Author: Vigneshwaran BS
๐ก Support
For questions, issues, or feature requests:
- Email: bs.vigneshwaran@gmail.com
- GitHub Issues: https://github.com/vigneshwaranbs/activity-tracker/issues
Made with โค๏ธ by Vigneshwaran BS