Browser RUM Integration
This guide walks through adding Real User Monitoring (RUM) to a browser application using the Kubesense SDK. It covers installation, initialization, and every instrumentation API available.
1. Prerequisites
Before integrating, obtain the following from your Kubesense workspace:
| Value | Where to find it |
|---|---|
applicationId | Settings → RUM Application Management → your application → View Details |
clientToken | Settings → RUM Application Management → your application → View Details |
2. Installation
Option A — npm / Yarn (recommended)
Full SDK (includes Session Replay):
npm install @kubesense/kubesense-browser-rum
# or
yarn add @kubesense/kubesense-browser-rumSlim SDK (no Session Replay, smaller bundle):
npm install @kubesense/kubesense-browser-rum-slim
# or
yarn add @kubesense/kubesense-browser-rum-slimOption B — CDN (script tag)
Add this script in the <head> of your HTML, before any other scripts:
<script>
(function(h,o,u,n,d){h=h[d]=h[d]||{q:[],onReady:function(c){h.q.push(c)}}
d=o.createElement(u);d.async=1;d.src=n
n=o.getElementsByTagName(u)[0];n.parentNode.insertBefore(d,n)
})(window,document,'script','<CDN_URL>/kubesense-rum.js','KUBESENSE_SDK')
</script>note: Replace <CDN_URL> with the URL provided by your Kubesense account team.
3. Initialization
Call init() once, as early as possible in your application's entry point. All API calls made before init() are queued and replayed automatically.
npm / ES Module
import { kubesenseSdk } from '@kubesense/kubesense-browser-rum'
kubesenseSdk.init({
applicationId: '<YOUR_APPLICATION_ID>',
clientToken: '<YOUR_CLIENT_TOKEN>',
site: 'app.kubesense.ai',
service: 'my-web-app',
env: 'production',
version: '1.0.0',
sessionSampleRate: 100,
trackUserInteractions: true,
trackResources: true,
trackLongTasks: true,
firstPartyDomains: [
"api.kubesense.ai", // exact hostname
/\.kubesense\.ai/, // all subdomains via regex
(host) => host.endsWith(".internal"), // custom function
],
})CDN
window.KUBESENSE_SDK.onReady(function() {
window.KUBESENSE_SDK.init({
applicationId: '<YOUR_APPLICATION_ID>',
clientToken: '<YOUR_CLIENT_TOKEN>',
site: 'app.kubesense.ai',
service: 'my-web-app',
env: 'production',
version: '1.0.0',
sessionSampleRate: 100,
trackUserInteractions: true,
trackResources: true,
trackLongTasks: true,
firstPartyDomains: [
"api.kubesense.ai",
/\.kubesense\.ai/,
(host) => host.endsWith(".internal"),
],
})
})React (App.tsx / main.tsx)
import { kubesenseSdk } from '@kubesense/kubesense-browser-rum'
kubesenseSdk.init({
applicationId: '<YOUR_APPLICATION_ID>',
clientToken: '<YOUR_CLIENT_TOKEN>',
site: 'app.kubesense.ai',
service: 'my-react-app',
env: process.env.NODE_ENV,
version: process.env.REACT_APP_VERSION,
sessionSampleRate: 100,
trackUserInteractions: true,
trackResources: true,
trackLongTasks: true,
firstPartyDomains: [
"api.kubesense.ai",
/\.kubesense\.ai/,
(host) => host.endsWith(".internal"),
],
})
function App() { /* ... */ }
export default App4. Configuration Reference
Required
| Option | Type | Description |
|---|---|---|
applicationId | string | Your RUM application ID |
clientToken | string | Your client authentication token |
Common Options
| Option | Type | Default | Description |
|---|---|---|---|
site | string | — | Your Kubesense instance hostname |
service | string | — | Service name for this application |
env | string | — | Deployment environment (production, staging, etc.) |
version | string | — | Application version (e.g. 1.2.3) |
sessionSampleRate | number | 100 | Percentage of sessions to collect (0–100) |
Tracking Behaviour
| Option | Type | Default | Description |
|---|---|---|---|
trackUserInteractions | boolean | true | Auto-collect clicks and user actions |
trackResources | boolean | true | Collect XHR / Fetch resource events |
trackLongTasks | boolean | true | Collect long task events (>50ms) |
trackViewsManually | boolean | false | Disable automatic view creation (SPA manual control) |
trackBfcacheViews | boolean | false | Create separate views for back/forward cache restores |
trackEarlyRequests | boolean | false | Capture requests made before SDK initialises |
trackAnonymousUser | boolean | true | Track anonymous user identity and extend cookie expiration |
actionNameAttribute | string | — | HTML attribute used to name actions (e.g. data-action-name) |
excludedActivityUrls | MatchOption[] | — | Request URLs to ignore for page activity calculation |
firstPartyDomains | MatchOption[] | — | Additional domains to treat as first-party in resource classification |
allowedTracingUrls | Array<MatchOption | TracingOption> | — | URLs to inject distributed trace headers into |
allowedGraphQlUrls | Array<MatchOption | GraphQlUrlOption> | — | GraphQL endpoints to track with operation metadata |
trackFeatureFlagsForEvents | FeatureFlagsForEvents[] | [] | Attach feature flag evaluations to these event types |
allowUntrustedEvents | boolean | false | Listen to programmatically dispatched DOM events (useful in automated test environments) |
allowedTrackingOrigins | MatchOption[] | — | Origins where the SDK is allowed to run (browser extension use) |
Session Replay & Privacy
| Option | Type | Default | Description |
|---|---|---|---|
sessionReplaySampleRate | number | 0 | Percentage of sessions to record (0–100) |
startSessionReplayRecordingManually | boolean | false if sessionReplaySampleRate > 0, else true | Start recording only when startSessionReplayRecording() is called |
defaultPrivacyLevel | 'mask' | 'mask-user-input' | 'allow' | 'mask' | Default text masking level for session replay |
enablePrivacyForActionName | boolean | false | Mask action names in session replay |
subdomain | string | — | Custom subdomain for session replay share links |
trackingConsent | 'granted' | 'not-granted' | 'granted' | Initial user tracking consent state |
defaultPrivacyLevel values
| Value | Behaviour |
|---|---|
'mask' | All text content and form inputs are replaced with xxx. Static and dynamic text is hidden. |
'mask-user-input' | Only form inputs (<input>, <textarea>, <select>) are masked. Static text is visible. |
'allow' | Nothing is masked. All content is recorded as-is. |
Override the privacy level on individual HTML elements using the data-ks-privacy attribute:
<!-- Force mask a specific element even when the global level is 'allow' -->
<div data-ks-privacy="mask">Card number: 4111 1111 1111 1111</div>
<!-- Allow a specific element even when the global level is 'mask' -->
<h1 data-ks-privacy="allow">Welcome to the Dashboard</h1>
<!-- Mask only user input within this container -->
<form data-ks-privacy="mask-user-input">
<input type="text" placeholder="Name" />
</form>Distributed Tracing
| Option | Type | Default | Description |
|---|---|---|---|
traceSampleRate | number | 100 | Percentage of requests to trace (0–100) |
traceContextInjection | 'sampled' | 'tracecontext' | 'sampled' | When to inject trace context into requests |
propagateTraceBaggage | boolean | false | Include user/account IDs in the W3C baggage header |
Performance & Transport
| Option | Type | Default | Description |
|---|---|---|---|
profilingSampleRate | number | 0 | Percentage of users to profile (0–100) |
compressIntakeRequests | boolean | false | Compress payloads sent to the intake (requires workerUrl) |
workerUrl | string | — | URL to the SDK Web Worker file (same origin required) |
proxy | string | function | — | Proxy URL or function to route SDK requests through a custom endpoint |
Session Persistence & Cookies
| Option | Type | Default | Description |
|---|---|---|---|
sessionPersistence | 'cookie' | 'local-storage' | 'cookie' | Storage strategy for sessions |
useSecureSessionCookie | boolean | false | Restrict session cookie to HTTPS connections only |
usePartitionedCrossSiteSessionCookie | boolean | false | Use a partitioned cross-site cookie — required when the SDK runs inside an iframe on another domain |
trackSessionAcrossSubdomains | boolean | false | Share the session across all subdomains of the same site |
storeContextsAcrossPages | boolean | false | Persist global context and user context in localStorage across page navigations |
note: sessionPersistence, useSecureSessionCookie, usePartitionedCrossSiteSessionCookie, and trackSessionAcrossSubdomains must be set to identical values if you use both the RUM and Logs SDKs on the same page.
Other Options
| Option | Type | Default | Description |
|---|---|---|---|
silentMultipleInit | boolean | false | Suppress the error when init() is called more than once on the same page |
firstPartyDomains — Detailed
By default, only the current page's hostname is classified as "first-party" in resource and trace attribution. Use firstPartyDomains to declare additional origins you own so they are correctly attributed instead of appearing as third-party.
Accepts strings (exact hostname), regular expressions, or functions:
kubesenseSdk.init({
// ...
firstPartyDomains: [
'api.myapp.com', // exact hostname
/\.myapp\.com$/, // all subdomains of myapp.com
(host) => host.endsWith('.internal'), // function — matches *.internal
],
})With this configuration, requests to api.myapp.com, cdn.myapp.com, or payments.internal are classified as first-party resources, and distributed trace headers are automatically injected if those origins also appear in allowedTracingUrls.
Callbacks & Plugins
| Option | Type | Description |
|---|---|---|
beforeSend | (event, context) => boolean | Inspect, modify, or discard events before sending. Return false to drop (not applicable to view events). |
plugins | RumPlugin[] | SDK plugin extensions |
beforeSend example — drop health-check errors
kubesenseSdk.init({
// ...
beforeSend: (event) => {
if (event.type === 'error' && event.error.message.includes('health-check')) {
return false // drop this event
}
return true
},
})beforeSend example — enrich resource events
kubesenseSdk.init({
// ...
beforeSend: (event) => {
if (event.type === 'resource' && event.resource.url.includes('/api/graphql')) {
event.context.graphql_operation = getGraphQlOperation(event.resource.url)
}
return true
},
})5. View Tracking
A view represents a page or screen the user is on. The SDK creates views automatically on URL changes. For Single-Page Apps with custom routing, you may want manual control.
Automatic (default)
Views are created automatically on every history.pushState or hashchange. No code needed.
Manual View Control (SPA)
Set trackViewsManually: true in init(), then call startView() on each route change.
// React Router example
import { useEffect } from 'react'
import { useLocation } from 'react-router-dom'
import { kubesenseSdk } from '@kubesense/kubesense-browser-rum'
function RouteTracker() {
const location = useLocation()
useEffect(() => {
kubesenseSdk.startView({ name: location.pathname })
}, [location])
return null
}// Vue Router example
router.afterEach((to) => {
kubesenseSdk.startView({ name: to.name ?? to.path })
})View API
// Start a new view with a name
kubesenseSdk.startView('checkout')
// Start with full options (name, service, version)
kubesenseSdk.startView({ name: 'checkout', service: 'payments', version: '2.1.0' })
// Change the name of the current view after it started
kubesenseSdk.setViewName('checkout-confirmation')
// Attach context to the current view only
kubesenseSdk.setViewContext({ cart_id: 'abc123', item_count: 3 })
kubesenseSdk.setViewContextProperty('experiment_variant', 'B')
// Read current view context
const ctx = kubesenseSdk.getViewContext()6. User Context
Attach user identity to all events. Call this after login or session restore.
// Set user (id is required)
kubesenseSdk.setUser({
id: 'usr-1234',
name: 'Jane Smith',
email: 'jane@example.com',
// Any custom properties:
plan: 'enterprise',
role: 'admin',
})
// Update a single property
kubesenseSdk.setUserProperty('plan', 'pro')
// Read current user
const user = kubesenseSdk.getUser()
// Remove a property
kubesenseSdk.removeUserProperty('role')
// Clear on logout
kubesenseSdk.clearUser()note: Calling setUser() with a different id automatically expires the current session to prevent cross-user data contamination on shared devices.
7. Account Context
Attach account/organization information to all events (useful for B2B applications).
kubesenseSdk.setAccount({
id: 'acct-5678',
name: 'Acme Corp',
plan: 'enterprise',
})
// Update a single property
kubesenseSdk.setAccountProperty('plan', 'enterprise-plus')
// Read current account
const account = kubesenseSdk.getAccount()
// Remove a property
kubesenseSdk.removeAccountProperty('plan')
// Clear
kubesenseSdk.clearAccount()8. Error Tracking
Automatic Error Collection
The SDK automatically captures:
- Unhandled JavaScript exceptions (
window.onerror) - Unhandled promise rejections
- Console errors (
console.error) - Network errors (for tracked resource URLs)
No code needed for these.
Manual Error Reporting
Report handled errors you catch yourself:
try {
processPayment(order)
} catch (error) {
kubesenseSdk.addError(error, {
order_id: order.id,
amount: order.total,
})
}// Report a plain string
kubesenseSdk.addError('Payment failed: insufficient funds', {
user_id: currentUser.id,
})React Error Boundary
import { Component } from 'react'
import { kubesenseSdk } from '@kubesense/kubesense-browser-rum'
class ErrorBoundary extends Component {
componentDidCatch(error: Error, info: React.ErrorInfo) {
kubesenseSdk.addError(error, {
component_stack: info.componentStack,
})
}
render() {
return this.props.children
}
}9. Action Tracking
Automatic Action Collection
When trackUserInteractions: true (default), the SDK automatically tracks clicks and names them from:
- The element's
data-action-nameattribute (configure viaactionNameAttribute) - The element's
aria-label,title, oraltattribute - The element's inner text (truncated)
Custom Actions
// Track a programmatic or non-click action
kubesenseSdk.addAction('export_csv', {
row_count: 1500,
format: 'csv',
})
kubesenseSdk.addAction('search', {
query: searchTerm,
results_count: results.length,
})Naming Clicks via HTML Attribute
<!-- Using default attribute (data-action-name) -->
<button data-action-name="Add to cart">+</button>
<!-- Using a custom attribute configured at init -->
<!-- kubesenseSdk.init({ actionNameAttribute: 'data-rum-action' }) -->
<button data-rum-action="checkout">Buy Now</button>10. Resource & Long Task Tracking
Both are enabled by default. No code needed.
kubesenseSdk.init({
trackResources: true, // default: true
trackLongTasks: true, // default: true
})To stop collecting resources or long tasks entirely:
kubesenseSdk.init({
trackResources: false,
trackLongTasks: false,
})11. Custom Timings & Performance Vitals
Custom View Timings
Mark key moments within the current view (e.g. "time to data loaded"):
// After data is fetched and rendered
kubesenseSdk.addTiming('data_ready')
// Pass an explicit timestamp (ms since page load)
kubesenseSdk.addTiming('hero_image_loaded', performance.now())Timings appear in the view event under view.custom_timings.
Duration Vitals
Measure elapsed time for arbitrary operations:
// Option 1: start / stop
const ref = kubesenseSdk.startDurationVital('checkout_flow')
// ... user completes checkout ...
kubesenseSdk.stopDurationVital(ref, {
context: { steps_completed: 4 },
})
// Option 2: start by name and stop by name
kubesenseSdk.startDurationVital('report_generation')
generateReport().then(() => {
kubesenseSdk.stopDurationVital('report_generation')
})
// Option 3: add a completed vital with known start/duration
kubesenseSdk.addDurationVital('pdf_render', {
startTime: renderStartTime,
duration: renderDuration,
context: { page_count: 12 },
})12. Feature Operations
Track multi-step operations (e.g. checkout flows, onboarding) as a series of steps with pass/fail outcomes.
// Start tracking a named operation
kubesenseSdk.startFeatureOperation('onboarding', {
context: { step: 'profile_setup' },
})
// Mark succeeded
kubesenseSdk.succeedFeatureOperation('onboarding', {
context: { steps_completed: 3 },
})
// Mark failed with a reason
kubesenseSdk.failFeatureOperation('onboarding', 'profile_incomplete', {
context: { missing_fields: ['phone'] },
})13. Global Context
Attach custom properties to every event (views, errors, resources, actions).
// Set at startup
kubesenseSdk.setGlobalContext({
deployment_region: 'us-east-1',
feature_set: 'beta',
})
// Add or update a single property at any time
kubesenseSdk.setGlobalContextProperty('ab_test_group', 'variant_B')
// Read current context
const ctx = kubesenseSdk.getGlobalContext()
// Remove one property
kubesenseSdk.removeGlobalContextProperty('feature_set')
// Clear everything
kubesenseSdk.clearGlobalContext()14. Session Replay
Session Replay records the user's screen so you can replay exactly what happened around an error or frustration signal.
Enable Replay
kubesenseSdk.init({
// ...
sessionReplaySampleRate: 20, // record 20% of sessions
})Manual Recording Control
kubesenseSdk.init({
// ...
sessionReplaySampleRate: 100,
startSessionReplayRecordingManually: true, // don't start automatically
})
// Start recording only for important flows (e.g. after login)
function onUserLoggedIn() {
kubesenseSdk.startSessionReplayRecording()
}
// Stop recording when leaving a sensitive screen
function onEnterPaymentPage() {
kubesenseSdk.stopSessionReplayRecording()
}Privacy Masking
kubesenseSdk.init({
defaultPrivacyLevel: 'mask-user-input', // 'mask' | 'mask-user-input' | 'allow'
})| Level | Behaviour |
|---|---|
mask | All text and inputs are masked (default) |
mask-user-input | Only form inputs are masked; static text is visible |
allow | Nothing is masked |
Override per element:
<!-- Force masking an element -->
<div data-ks-privacy="mask">Sensitive data</div>
<!-- Allow a specific element in a masked view -->
<p data-ks-privacy="allow">Public content</p>Get Replay Link
const replayUrl = kubesenseSdk.getSessionReplayLink()
// Share or store this URL to view the recording later15. Distributed Tracing
Correlate frontend RUM events with backend traces by injecting trace headers into outgoing requests.
kubesenseSdk.init({
// ...
allowedTracingUrls: [
'https://api.example.com', // exact origin
/https:\/\/.*\.example\.com/, // regex
(url) => url.startsWith('https://api.'), // function
],
traceSampleRate: 100, // trace 100% of allowed requests
})Custom Propagator Format
kubesenseSdk.init({
allowedTracingUrls: [
{
match: 'https://api.example.com',
propagatorTypes: ['tracecontext', 'kubesense'], // W3C + Kubesense headers
},
],
})Propagate User/Account in Baggage
kubesenseSdk.init({
propagateTraceBaggage: true, // includes user.id and account.id in the baggage header
})16. Privacy & Consent
Tracking Consent
If you require explicit consent before collecting data (e.g. GDPR):
kubesenseSdk.init({
// ...
trackingConsent: 'not-granted', // start with no collection
})
// After user accepts your consent banner:
kubesenseSdk.setTrackingConsent('granted')
// If user withdraws consent:
kubesenseSdk.setTrackingConsent('not-granted')Stop a Session
kubesenseSdk.stopSession()
// No new events collected until next page load or user activity17. Feature Flag Tracking
Track which feature flags were evaluated during a session or action:
// After evaluating a flag
const flagValue = featureFlags.get('new_checkout')
kubesenseSdk.addFeatureFlagEvaluation('new_checkout', flagValue)To attach flag evaluations to specific event types, configure at init:
kubesenseSdk.init({
trackFeatureFlagsForEvents: ['action', 'vital', 'long_task', 'resource'],
})18. GraphQL Tracking
Track GraphQL requests with operation metadata:
kubesenseSdk.init({
allowedGraphQlUrls: [
{
match: 'https://api.example.com/graphql',
trackPayload: false, // don't capture request body
trackResponseErrors: true, // capture GraphQL errors in response
},
],
})19. TypeScript Support
The SDK ships full TypeScript types. Import them directly:
import type {
RumInitConfiguration,
RumEvent,
RumEventDomainContext,
} from '@kubesense/kubesense-browser-rum'
const config: RumInitConfiguration = {
applicationId: '<YOUR_APPLICATION_ID>',
clientToken: '<YOUR_CLIENT_TOKEN>',
service: 'my-app',
env: 'production',
beforeSend: (event: RumEvent, context: RumEventDomainContext): boolean => {
// type-safe access to event fields
return true
},
}Quick Reference
// Initialization
kubesenseSdk.init({ applicationId, clientToken, /* ... */ })
// Views
kubesenseSdk.startView(name)
kubesenseSdk.setViewName(name)
kubesenseSdk.setViewContext(context)
kubesenseSdk.setViewContextProperty(key, value)
// User & Account
kubesenseSdk.setUser({ id, name, email, /* ...custom */ })
kubesenseSdk.clearUser()
kubesenseSdk.setAccount({ id, name, /* ...custom */ })
kubesenseSdk.clearAccount()
// Global Context
kubesenseSdk.setGlobalContext(context)
kubesenseSdk.setGlobalContextProperty(key, value)
kubesenseSdk.clearGlobalContext()
// Errors & Actions
kubesenseSdk.addError(error, context)
kubesenseSdk.addAction(name, context)
// Timings & Vitals
kubesenseSdk.addTiming(name)
kubesenseSdk.startDurationVital(name)
kubesenseSdk.stopDurationVital(nameOrRef)
// Session Replay
kubesenseSdk.startSessionReplayRecording()
kubesenseSdk.stopSessionReplayRecording()
kubesenseSdk.getSessionReplayLink()
// Consent
kubesenseSdk.setTrackingConsent('granted' | 'not-granted')
kubesenseSdk.stopSession()