Docs/JavaScript SDK

JavaScript SDK

Privacy-first session recording for web applications

Overview

The LogSpace JavaScript SDK is a developer-friendly, privacy-first session recording library that you can embed directly in your web application. Unlike the Chrome Extension, the SDK runs inside your app and provides programmatic control over recording.

Privacy-First

Built-in PII masking and configurable privacy controls

Smart Sampling

Capture context before errors with rolling buffer

Framework Agnostic

Works with React, Vue, Angular, or vanilla JS

Installation

NPM / Yarn / Bun

# Using npm
npm install @logspace/sdk
# Using yarn
yarn add @logspace/sdk
# Using bun
bun add @logspace/sdk

Quick Start

Basic Usage

import LogSpace from '@logspace/sdk';
LogSpace.init({
serverUrl: 'https://your-logspace-server.com',
apiKey: 'your-api-key',
});

With Smart Sampling

Only record sessions where errors occur, capturing context before the error:

LogSpace.init({
serverUrl: 'https://your-logspace-server.com',
apiKey: 'your-api-key',
sampling: {
enabled: true,
bufferBefore: 10, // Keep 10 seconds before trigger
recordAfter: 10, // Continue 10 seconds after
triggers: {
onError: true, // Uncaught errors
onConsoleError: true, // console.error
onNetworkStatus: [500, 502, 503], // Server errors
},
},
});

With Privacy Controls

LogSpace.init({
serverUrl: 'https://your-logspace-server.com',
apiKey: 'your-api-key',
privacy: {
maskSensitiveData: true, // Auto-mask PII
maskSelectors: ['.credit-card', '[data-sensitive]'],
excludeUrls: [/api\/auth/, /stripe\.com/],
blockNetworkBodies: ['/api/payments'],
redactHeaders: ['authorization', 'x-api-key'],
},
});

With Recording Quality Preset

Control recording fidelity vs file size with a simple preset:

LogSpace.init({
serverUrl: 'https://your-logspace-server.com',
apiKey: 'your-api-key',
recordingQuality: 'low', // 'low' | 'medium' | 'high' | 'extrahigh'
});
low

Smallest files, basic replay. Good for very long sessions.

medium

Smaller files, less frequent snapshots. Good for long sessions.

high

Balanced quality and size. Recommended default.

extrahigh

Full fidelity, larger files. Best for detailed debugging.

Configuration Reference

Complete Configuration with Defaults

All configuration options are optional. Here are the defaults:

1LogSpace.init({
2 // Server Configuration
3 serverUrl: undefined, // No server - sessions stored locally only
4 apiKey: undefined, // No authentication
5 headers: {}, // Additional headers for API requests
6
7 // Recording Quality Preset - simple way to control size vs fidelity
8 recordingQuality: 'medium', // 'low' | 'medium' | 'high' | 'extrahigh'
9
10 // Capture Configuration - what to record
11 capture: {
12 rrweb: true, // ✓ DOM recording for visual replay
13 console: true, // ✓ Console logs
14 network: true, // ✓ Network requests (XHR/Fetch)
15 errors: true, // ✓ JavaScript errors
16 interactions: true, // ✓ User clicks/inputs
17 performance: true, // ✓ Performance metrics
18 websocket: true, // ✓ WebSocket connections
19 sse: true, // ✓ Server-Sent Events
20 storage: true, // ✓ localStorage/sessionStorage changes
21 },
22
23 // RRWeb (DOM Recording) Configuration
24 rrweb: {
25 maskAllInputs: false, // Mask input values for privacy
26 maskTextSelector: undefined, // CSS selector for masking text
27 blockSelector: undefined, // CSS selector for blocking elements
28 ignoreSelector: undefined, // CSS selector for ignoring elements
29 recordCanvas: false, // Don't record canvas (expensive)
30 checkoutEveryNth: 500, // Full snapshot every 500 events (medium quality)
31 },
32
33 // Privacy Configuration
34 privacy: {
35 maskSensitiveData: true, // Auto-mask emails, phones, SSN, cards
36 maskSelectors: [], // Additional CSS selectors to mask
37 excludeUrls: [], // URL patterns to exclude (regex)
38 blockNetworkBodies: [], // URLs to block request/response bodies
39 redactHeaders: [], // Header names to redact
40 logLevels: ['log', 'info', 'warn', 'error', 'debug'], // All levels
41 },
42
43 // Session Limits - protection against memory issues
44 limits: {
45 maxLogs: 10000, // Stop after 10,000 logs
46 maxSize: 50 * 1024 * 1024, // Stop at 50MB uncompressed (gzipped before upload)
47 maxDuration: 1800, // Stop after 30 minutes
48 idleTimeout: 120, // Auto-end after 2 minutes of no activity
49 rateLimit: 100, // Max 100 logs per second
50 deduplicate: true, // Collapse duplicate consecutive logs
51 maxNetworkBodySize: 10 * 1024, // 10KB max for request/response bodies
52 },
53
54 // Auto-End Configuration
55 autoEnd: {
56 continueOnRefresh: true, // Continue same session across page refreshes
57 onIdle: true, // Auto-end on idle timeout
58 onLimitReached: true, // Auto-end when limits hit
59 },
60
61 // Lifecycle Hooks - all optional
62 hooks: {
63 onSessionStart: undefined, // (session) => void
64 onSessionEnd: undefined, // (session, logs) => void
65 onLimitReached: undefined, // (reason) => void
66 onAction: undefined, // (log, session) => void
67 beforeSend: undefined, // (logs) => logs | null
68 onError: undefined, // (error) => void
69 },
70
71 // Development Options
72 debug: false, // Disable debug logging
73 dryRun: false, // Actually send data (not dry run)
74});

Nothing is required!

You can initialize with an empty config:

LogSpace.init({}); // Works! Sessions stored locally

Recommended minimum for production:

LogSpace.init({
serverUrl: 'https://your-server.com', // To send sessions to server
apiKey: 'sdk_live_xxxxx', // From Admin Dashboard → Settings → API Keys
});

Server Configuration

serverUrlstring | undefined

URL of your LogSpace API server. If not provided, sessions are stored locally only.

apiKeystring | undefined

SDK API key (starts with sdk_live_) for authentication. Generate your API key in the Admin Dashboard → Settings → API Keys. The key is shown only once at creation time, so save it securely.

headersRecord<string, string>

Additional headers to include in API requests (e.g., custom auth).

Recording Quality Preset

Simple way to control the tradeoff between recording fidelity and file size. The preset adjusts rrweb settings like checkoutEveryNth.

recordingQualitydefault: 'medium'

Choose a quality preset: 'low', 'medium', 'high', or 'extrahigh'

low

checkoutEveryNth: 1000

Smallest files, basic replay

medium

checkoutEveryNth: 500

Smaller files, good for long sessions

high

checkoutEveryNth: 300

Balanced (recommended)

extrahigh

checkoutEveryNth: 150

Full fidelity, larger files

Note: Your explicit rrweb config always overrides preset values.

Capture Options

Configure what data to record (all enabled by default):

capture.rrwebdefault: true

DOM recording for visual replay using rrweb library

capture.consoledefault: true

Console logs (log, info, warn, error, debug)

capture.networkdefault: true

XHR and Fetch requests with timing and payloads

capture.errorsdefault: true

JavaScript errors with stack traces

capture.interactionsdefault: true

User clicks, inputs, scrolls, and navigation

capture.performancedefault: true

Performance metrics (LCP, FID, CLS, TTFB)

capture.websocketdefault: true

WebSocket connections and messages

capture.ssedefault: true

Server-Sent Events streams

capture.storagedefault: true

localStorage, sessionStorage, cookie, and IndexedDB operations

RRWeb (DOM Recording) Configuration

Fine-tune DOM recording behavior:

rrweb.maskAllInputsdefault: false

Mask all input field values with asterisks for privacy.

rrweb.maskTextSelectordefault: undefined

CSS selector for elements whose text content should be masked.
Example: ".user-name, .email-address"

rrweb.blockSelectordefault: undefined

CSS selector for elements to completely hide (replaced with placeholder).
Example: ".credit-card-form, [data-private]"

rrweb.ignoreSelectordefault: undefined

CSS selector for elements to ignore completely (not recorded at all).

rrweb.recordCanvasdefault: false

Record canvas elements. ⚠️ Can be expensive for performance.

rrweb.checkoutEveryNthdefault: 150

Take a full DOM snapshot every N events. Lower = larger recordings but faster seeking.

Privacy Configuration

Built-in privacy controls for sensitive data:

privacy.maskSensitiveDatadefault: true

Auto-detect and mask emails, phone numbers, SSNs, and credit card numbers.

privacy.maskSelectorsdefault: []

Additional CSS selectors for elements to mask.
Example: [".credit-card", "[data-sensitive]"]

privacy.excludeUrlsdefault: []

URL patterns (regex) to exclude from network logging.
Example: [/api\/auth/, /stripe\.com/]

privacy.blockNetworkBodiesdefault: []

URLs where request/response bodies should not be captured.
Example: ["/api/payments", "/api/auth/login"]

privacy.redactHeadersdefault: []

HTTP header names to redact from network logs.
Example: ["authorization", "x-api-key", "cookie"]

privacy.logLevelsdefault: all levels

Which console log levels to capture.
Options: ["log", "info", "warn", "error", "debug"]

Sampling Configuration

Smart sampling keeps a rolling buffer and only saves sessions when triggers fire:

sampling: {
enabled: true, // Enable sampling mode
bufferBefore: 10, // Keep 10 seconds of logs before trigger
recordAfter: 10, // Continue recording 10 seconds after
triggers: {
onError: true, // Trigger on uncaught JavaScript errors
onConsoleError: true, // Trigger on console.error() calls
onNetworkStatus: [500, 502, 503, 504], // Trigger on HTTP errors
},
}
sampling.enableddefault: false

Enable sampling mode. When enabled, sessions only save on triggers.

sampling.bufferBeforedefault: 30

Seconds of logs to keep before the trigger event.

sampling.recordAfterdefault: 30

Seconds to continue recording after the trigger event.

sampling.triggers.onErrordefault: true

Trigger on uncaught JavaScript errors (window.onerror).

sampling.triggers.onConsoleErrordefault: true

Trigger when console.error() is called.

sampling.triggers.onNetworkStatusdefault: [500, 502, 503]

Array of HTTP status codes that trigger recording.

Session Limits

Protect against memory issues and runaway sessions:

limits.maxLogsdefault: 10,000

Maximum number of log entries before session auto-ends.

limits.maxSizedefault: 50MB

Maximum uncompressed session size in bytes. Data is gzipped before upload.

limits.maxDurationdefault: 1800

Maximum session duration in seconds (30 minutes).

limits.idleTimeoutdefault: 120

Seconds of inactivity before auto-ending session (2 minutes).

limits.rateLimitdefault: 100

Maximum logs per second. Excess logs are dropped.

limits.deduplicatedefault: true

Collapse duplicate consecutive log entries.

limits.maxNetworkBodySizedefault: 10KB

Maximum size in bytes for network request/response bodies. Larger bodies are truncated.

Auto-End Configuration

Control when sessions automatically end:

autoEnd.continueOnRefreshdefault: true

Continue the same session across page refreshes (uses sessionStorage).

autoEnd.onIdledefault: true

Automatically end session when idle timeout is reached.

autoEnd.onLimitReacheddefault: true

Automatically end session when any limit is hit.

Lifecycle Hooks

Customize SDK behavior with optional hooks. All hooks are optional:

hooks: {
// Called on every captured action
onAction: (log, session) => {
console.log('Captured:', log.type, log.data);
},
// Transform/filter logs before sending - return null to drop all
beforeSend: (logs) => {
return logs.filter(log => log.type !== 'performance');
},
// Called when session starts
onSessionStart: (session) => {
analytics.track('logspace_session_started', { id: session.id });
},
// Called when session ends
onSessionEnd: (session, logs) => {
console.log(`Session ended with ${logs.length} logs`);
},
// Called when a limit is reached
onLimitReached: (reason) => {
// reason: 'maxLogs' | 'maxSize' | 'maxDuration' | 'idle'
console.warn('Session ended:', reason);
},
// Called on transport/network errors
onError: (error) => {
Sentry.captureException(error);
},
}
hooks.onAction(log, session) => void

Called on every captured log entry.

hooks.beforeSend(logs) => logs | null

Transform or filter logs before sending. Return null to drop.

hooks.onSessionStart(session) => void

Called when a new session begins.

hooks.onSessionEnd(session, logs) => void

Called when session ends with all captured logs.

hooks.onLimitReached(reason) => void

Called when session auto-ends due to limits (maxLogs, maxSize, maxDuration, idle).

hooks.onError(error) => void

Called on transport/network errors when sending data.

hooks.onSamplingTrigger(trigger, log?) => void

Called when sampling is triggered (error, consoleError, networkError, manual).

hooks.onSessionDiscarded(session, reason) => void

Called when a sampling session is discarded (no trigger occurred). Reason: noTrigger, idle, maxDuration, manual.

Development Options

debugdefault: false

Enable verbose console logging for debugging SDK behavior.

dryRundefault: false

When true, captures data but doesn't send to server. Useful for testing.

API Reference

LogSpace.identify(userId, traits?)

Identify the current user for session attribution:

LogSpace.identify('user-123', {
plan: 'premium',
company: 'Acme Inc',
});

LogSpace.track(event, properties?)

Track custom events:

LogSpace.track('button_clicked', {
buttonId: 'checkout',
page: '/cart',
});

LogSpace.breadcrumb(message, category?)

Add navigation breadcrumbs:

LogSpace.breadcrumb('User navigated to checkout', 'navigation');

LogSpace.trigger(reason)

Manually trigger recording (when using sampling mode):

LogSpace.trigger('user_reported_issue');

Session Control

startSession(metadata?)

Start a new recording session

stopSession()

Stop the current session

pauseRecording()

Pause recording temporarily

resumeRecording()

Resume paused recording

getSession()

Get current session info

getSessionLogs()

Get all logs for current session

getConfig()

Get current configuration (read-only)

setConfig(updates)

Update config at runtime (rateLimit, maxLogs, maxSize, idleTimeout, debug)

Framework Integration

React

App.tsx
import { useEffect } from 'react';
import LogSpace from '@logspace/sdk';
function App() {
useEffect(() => {
LogSpace.init({
serverUrl: 'https://your-server.com',
apiKey: 'your-api-key',
});
return () => LogSpace.destroy();
}, []);
return <YourApp />;
}

Next.js (App Router)

app/providers.tsx
'use client';
import { useEffect } from 'react';
import LogSpace from '@logspace/sdk';
export function LogSpaceProvider({ children }) {
useEffect(() => {
LogSpace.init({
serverUrl: 'https://your-server.com',
apiKey: 'your-api-key',
});
return () => LogSpace.destroy();
}, []);
return <>{children}</>;
}

Vue 3

main.ts
import { createApp } from 'vue';
import LogSpace from '@logspace/sdk';
import App from './App.vue';
LogSpace.init({
serverUrl: import.meta.env.VITE_LOGSPACE_URL,
apiKey: import.meta.env.VITE_LOGSPACE_API_KEY,
});
createApp(App).mount('#app');

Offline Support

The SDK automatically queues logs when offline and syncs when the connection is restored:

When Offline

Logs are stored in IndexedDB with configurable limits

When Online

Queued logs are automatically uploaded in batches

Session Recovery

The SDK uses multiple layers of persistence to ensure session data is never lost:

1

Periodic Checkpoints

Every 15 seconds, session data is saved to IndexedDB. If the browser crashes, this checkpoint is recovered on next page load.

2

Emergency Backup

On page unload, data is saved to localStorage (synchronous, reliable). This ensures data survives even if IndexedDB operations don't complete.

3

Pending Queue

If sending fails (network error, server down), sessions are queued for automatic retry with exponential backoff.

Session End Reasons

Each session includes an endReason field indicating how it ended:

End ReasonDescription
manualSession stopped via stopSession() or destroy()
idleAuto-ended due to idle timeout (no user activity)
maxLogsHit limits.maxLogs threshold
maxSizeHit limits.maxSize threshold
maxDurationHit limits.maxDuration threshold
unloadPage is being unloaded (navigation, refresh, close)
navigateAwayTab was hidden for longer than limits.navigateAwayTimeout
crashRecovered from browser crash (no cleanup code ran)
samplingTriggeredSampling mode session ended after trigger + recordAfter

Bundle Sizes

FormatSizeGzipped
ESM80.5 KB16.5 KB
UMD39.1 KB11.5 KB
IIFE39.0 KB11.4 KB

Troubleshooting

Sessions not appearing in dashboard▼
  1. Check that serverUrl and apiKey are correct
  2. Enable debug: true to see console logs
  3. Check browser network tab for failed requests
  4. Verify CORS headers allow your domain
  5. Make sure session has logs (empty sessions aren't sent)
TypeScript autocomplete not working▼

Make sure you're importing correctly:

// ✓ Correct
import LogSpace from '@logspace/sdk';
// ✗ Wrong - won't have types
import LogSpace from '@logspace/sdk/logspace.esm.js';
Memory issues / performance problems▼

If the SDK uses too much memory:

  • Lower limits.maxLogs (default: 10,000)
  • Lower limits.maxSize (default: 50MB uncompressed)
  • Disable capture.rrweb (DOM recording is expensive)
  • Use privacy.excludeUrls to skip high-traffic endpoints
  • Enable sampling mode for error-only recording