Real User Monitoring
Track what your users actually experience. Sessions, Web Vitals, errors, clicks, and optional session replay.
Installation
npm install vident-browserQuick Start
import { createVidentBrowser } from 'vident-browser'
const vident = createVidentBrowser({
apiKey: 'vd_...',
appName: 'my-app',
})
// Identify users (optional)
vident.setUser('user-123', { plan: 'pro', company: 'Acme' })
// Track custom events
vident.trackEvent('checkout_started', { cartValue: 99.99 })Configuration
createVidentBrowser({
// Required
apiKey: 'vd_...',
// Optional
appName: 'my-app', // Identifies your app
sampleRate: 1, // 0-1, default 1 (100%)
// Auto-tracking (all true by default)
trackPageViews: true, // SPA navigation included
trackClicks: true, // Interactive elements
trackVitals: true, // Core Web Vitals
trackErrors: true, // Unhandled errors
trackResources: true, // Fetch/XHR timing
// Session storage
sessionStorage: 'cookie', // 'cookie' | 'sessionStorage'
// Distributed tracing
tracing: { ... },
// Session replay
replay: { ... },
})What's Tracked Automatically
Page Views
Tracked on initial load and SPA navigation (pushState, replaceState, popstate).
Clicks
Tracked on interactive elements: buttons, links, inputs, and elements with role="button".
Core Web Vitals
All six metrics sent once per session:
| Metric | What it measures | |--------|------------------| | LCP | Largest Contentful Paint - loading performance | | FID | First Input Delay - interactivity (legacy) | | INP | Interaction to Next Paint - responsiveness | | CLS | Cumulative Layout Shift - visual stability | | TTFB | Time to First Byte - server response | | FCP | First Contentful Paint - perceived speed |
Errors
Unhandled errors and promise rejections captured with stack traces.
HTTP Resources
Fetch and XHR requests tracked with timing and status codes.
Dashboard Features
Once data flows in, the Vident dashboard gives you:
- Errors — Auto-grouped by stack trace with affected sessions
- Sessions — Filterable session list with replay, searchable by user or event
- Users — User profiles with session history, error counts, and device info
- Events — Custom event explorer with trends and property breakdowns
- Funnels — URL-based conversion funnels with step-by-step drop-off
- Web Vitals — LCP, CLS, INP per page with geographic and device segmentation
Manual Tracking
// Custom events
vident.trackEvent('purchase_completed', {
orderId: 'abc-123',
total: 149.99,
items: 3,
})
// Manual page views (usually auto-tracked)
vident.trackPageView('/custom-route')
// Manual error capture with trace correlation
vident.trackError(new Error('Payment failed'), {
requestId: 'req-456',
traceId: 'abc123def456',
})User Identification
// Set user with ID only
vident.setUser('user-123')
// Set user with traits
vident.setUser('user-123', {
email: '[email protected]',
plan: 'enterprise',
company: 'Acme Corp',
})Session Storage Modes
// Cookie mode (default) - survives OAuth redirects
createVidentBrowser({
apiKey: 'vd_...',
sessionStorage: 'cookie',
})
// sessionStorage mode - tab-isolated, no cookie consent needed
createVidentBrowser({
apiKey: 'vd_...',
sessionStorage: 'sessionStorage',
})Cookie mode: Session survives page refreshes and OAuth redirects. Uses SameSite=Lax.
sessionStorage mode: Isolated per tab. Cleared when tab closes. No cookies written.
Distributed Tracing
Connect frontend requests to backend traces.
createVidentBrowser({
apiKey: 'vd_...',
tracing: {
enabled: true, // default
traceFetch: true, // Trace fetch requests
traceXHR: true, // Trace XHR requests
propagateToOrigins: [
/\.mycompany\.com$/, // Same root domain (default)
'api.partner.com', // Explicit allowlist
],
},
})Automatically injects W3C traceparent headers to allowlisted origins.
Session Replay
Record user sessions for debugging. Privacy-first with configurable masking.
createVidentBrowser({
apiKey: 'vd_...',
replay: {
enabled: true,
sampleRate: 0.1, // 10% of sessions uploaded
onErrorSampleRate: 1.0, // 100% when error occurs
privacyMode: 'strict', // 'strict' | 'balanced' | 'permissive'
},
})Privacy Modes
| Mode | Text | Inputs | Use case | |------|------|--------|----------| | strict | Masked | Masked | Default, GDPR-safe | | balanced | Visible | Masked | Better context | | permissive | Visible | Visible | Internal tools only |
HTML Privacy Attributes
<!-- Block element entirely -->
<div data-vd-block>Sensitive content</div>
<!-- Mask text with asterisks -->
<span data-vd-mask>[email protected]</span>
<!-- Show content even in strict mode -->
<p data-vd-unmask>Public announcement</p>Force Upload for VIP Users
// Always capture replay for important users
if (user.plan === 'enterprise') {
vident.forceReplayUpload()
}Framework Integration
Next.js
// next.config.ts
import { withVident } from 'vident-browser/next'
export default withVident({})
// app/providers.tsx — initialize the SDK
'use client'
import { createVidentBrowser } from 'vident-browser'
import { useEffect } from 'react'
let vident: ReturnType<typeof createVidentBrowser> | null = null
export function RumProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
if (!vident) {
vident = createVidentBrowser({
apiKey: process.env.NEXT_PUBLIC_VIDENT_API_KEY!,
})
}
}, [])
return children
}
// app/layout.tsx
import { RumProvider } from './providers'
export default function RootLayout({ children }) {
return (
<html>
<body>
<RumProvider>{children}</RumProvider>
</body>
</html>
)
}Script Tag
<script type="module">
import { createVidentBrowser } from 'https://esm.sh/vident-browser'
window.vident = createVidentBrowser({
apiKey: 'vd_...',
appName: 'my-site',
})
</script>API Reference
Client Methods
| Method | Description |
|--------|-------------|
| trackEvent(name, props?) | Track custom event |
| trackPageView(url?) | Manual page view |
| trackError(error, opts?) | Manual error capture |
| setUser(id, traits?) | Identify user |
| getSessionId() | Get current session ID |
| forceReplayUpload() | Force replay upload |
| stopReplay() | Stop replay recording |
Session Functions
import { getSessionId, endSession } from 'vident-browser'
// Get current session ID (or null if expired)
const sessionId = getSessionId()
// Manually end session (starts new one on next event)
endSession()Sampling
createVidentBrowser({
apiKey: 'vd_...',
sampleRate: 0.5, // Only 50% of sessions tracked
})When sampled out, the client returns a no-op implementation with zero overhead.