Real User Monitoring SDK for Vident - track sessions, page views, clicks, and Core Web Vitals
npm install vident-rumReal User Monitoring SDK for browser applications. Captures page views, clicks, errors, Web Vitals, and session replays.
``bash`
npm install @spanwise/rumor
pnpm add @spanwise/rumor
yarn add @spanwise/rum
`typescript
import { createSpanwiseBrowser } from "@spanwise/rum"
const spanwise = createSpanwiseBrowser({
apiKey: "sw_...",
serviceName: "my-app",
})
// That's it! Page views, clicks, errors, and vitals are tracked automatically.
`
`typescript
const spanwise = createSpanwiseBrowser({
// Required
apiKey: "sw_...",
// Optional
serviceName: "my-app", // Service name for filtering
sampleRate: 1.0, // Sample 100% of sessions (default: 1.0)
trackPageViews: true, // Track page views (default: true)
trackClicks: true, // Track clicks (default: true)
trackVitals: true, // Track Web Vitals (default: true)
trackErrors: true, // Track errors (default: true)
trackResources: true, // Track fetch/XHR requests (default: true)
sessionStorage: "cookie", // "cookie" or "sessionStorage" (default: "cookie")
// Distributed Tracing
tracing: {
enabled: true, // Inject traceparent headers (default: true)
propagateToOrigins: [ // Origins to propagate trace context to
"https://api.example.com",
/\.example\.com$/,
],
},
// Session Replay (opt-in)
replay: {
enabled: true, // Enable replay recording (default: false)
sampleRate: 0.1, // Record 10% of sessions (default: 0.1)
onErrorSampleRate: 1.0, // Record 100% of sessions with errors (default: 1.0)
privacyMode: "strict", // Privacy mode (default: "strict")
},
})
`
Track a custom event.
`typescript`
spanwise.trackEvent("purchase", {
productId: "123",
amount: 99.99,
})
Manually track a page view. Called automatically on navigation.
`typescript`
spanwise.trackPageView("/checkout")
Manually track an error.
`typescript`
try {
await riskyOperation()
} catch (error) {
spanwise.trackError(error, {
requestId: "req_123",
traceId: "trace_abc",
})
}
Identify the current user.
`typescript`
spanwise.setUser("user_123", {
email: "user@example.com",
plan: "pro",
})
Get the current session ID.
`typescript`
const sessionId = spanwise.getSessionId()
Force replay upload for specific users (bypasses sampling).
`typescript`
// Force upload for VIP users regardless of sample rate
if (user.isVIP) {
spanwise.forceReplayUpload()
}
Stop replay recording entirely.
Check if replay is currently uploading to server (vs buffering in memory).
---
Session Replay records user interactions as a video-like playback. It captures DOM mutations, mouse movements, clicks, and scrolls.
`typescript`
const spanwise = createSpanwiseBrowser({
apiKey: "sw_...",
replay: {
enabled: true,
},
})
Session Replay uses a rolling buffer architecture (similar to Sentry):
1. Always recording - When enabled, the SDK always records to a 60-second memory buffer
2. Sampled sessions (default 10%) - Upload immediately to server
3. Non-sampled sessions (90%) - Keep buffer in memory, zero network cost
4. On error - Flush the 60-second buffer and start uploading
This means you always capture what happened before an error, not just after.
`typescript`
replay: {
enabled: true,
sampleRate: 0.1, // 10% upload immediately
onErrorSampleRate: 1.0, // 100% upload on error (includes 60s buffer)
}
| Scenario | What's Captured |
|----------|-----------------|
| Sampled (10%) | Full session from start |
| Not sampled + error | 60 seconds before error + everything after |
| Not sampled + no error | Nothing sent (zero network cost) |
---
Session Replay is designed with privacy as the default. No configuration is required for most applications - sensitive data is automatically protected.
| Mode | Text | Inputs | Use Case |
|------|------|--------|----------|
| "strict" (default) | Masked | Masked | Most applications |"balanced"
| | Visible | Masked | Marketing sites, blogs |"permissive"
| | Visible | Visible | Internal tools only |
`typescript`
replay: {
enabled: true,
privacyMode: "strict", // Default - masks everything
}
#### Strict Mode (Default)
All text content is replaced with **. Form inputs show •••••. You see the page structure and user interactions, but no actual content.
``
┌─────────────────────────────┐
│ * * │ ← Masked heading
│ │
│ * │ ← Masked paragraph
│ ** │
│ │
│ Email: [•••••••••••••] │ ← Masked input
│ Password: [••••••••] │
│ │
│ [] │ ← Masked button
└─────────────────────────────┘
#### Balanced Mode
Text is visible, but all form inputs are masked. Good for content-heavy sites.
``
┌─────────────────────────────┐
│ Welcome Back │ ← Visible heading
│ │
│ Please sign in to continue │ ← Visible text
│ │
│ Email: [•••••••••••••] │ ← Masked input
│ Password: [••••••••] │
│ │
│ [Sign In] │ ← Visible button
└─────────────────────────────┘
#### Permissive Mode
Everything is recorded as-is. Only use for internal tools or with explicit user consent.
Regardless of privacy mode, you can control specific elements:
#### Block Elements Completely
Use data-sw-block to completely hide an element. It will appear as a gray placeholder in replays.
`html
#### Mask Specific Elements
Use
data-sw-mask to mask text while preserving structure (useful in balanced/permissive modes).`html
Contact: user@example.com
`#### Unmask Elements in Strict Mode
Use
data-sw-unmask to show content even when using strict mode.`html
`#### rrweb Classes (Alternative)
The standard rrweb classes also work:
`html
Hidden content
Masked content
`$3
1. Start with strict mode - It's the safest default. Only relax if needed.
2. Block sensitive sections entirely - For areas with PII (profile pages, account settings), use
data-sw-block.3. Review before production - Test your app with replay enabled and verify no sensitive data appears.
4. Consider user consent - For GDPR compliance, inform users that sessions may be recorded.
5. Use unmask sparingly - Only unmask truly static, non-sensitive content like navigation labels.
$3
Even without configuration:
- All
values are masked (except in permissive mode)
- Password fields are always masked
- Credit card patterns are detected and masked
-