sessionable
v0.5.0
Published
Session recording SDK for Sessionable
Maintainers
Readme
Sessionable SDK
Session recording and replay SDK for Sessionable, allowing you to capture user sessions and send them to your Sessionable backend.
Installation
npm install sessionable
# or
yarn add sessionableQuick Start
import { Sessionable } from "sessionable";
// Initialize the SDK
const sessionable = new Sessionable({
apiKey: "your-api-key",
});
// Start recording automatically
sessionable.start();
// Later, stop recording
// sessionable.stop();React Component
For React applications, we provide a convenient component:
import { SessionRecorder, useSessionable } from "sessionable/react";
function App() {
return (
<div>
<SessionRecorder apiKey="your-api-key" autoStart={true}>
<YourApp />
</SessionRecorder>
</div>
);
}
// In child components, you can access the sessionable instance:
function YourApp() {
// This hook provides stable access to the Sessionable instance with
// optimized rendering - it won't re-render when metrics update
const { isRecording, identify } = useSessionable();
const handleUserLogin = async (userId, orgId) => {
// Identify the user when they log in
if (isRecording) {
await identify(userId, orgId);
}
};
return <div>{/* Your app content */}</div>;
}Configuration Options
The SDK accepts the following configuration options:
| Option | Type | Description | Default |
| ------------------ | --------- | ---------------------------------------------------- | ----------- |
| apiKey | string | Your Sessionable API key | Required |
| autoStart | boolean | Start recording automatically | false |
| metadata | Object | Custom metadata to include | {} |
| maskInputs | boolean | Whether to mask input fields (passwords, text, etc.) | false |
| maskTextSelector | string | CSS selector for text elements that should be masked | undefined |
| debug | boolean | Enable debug mode (uses localhost:5173 endpoint) | false |
| useDeviceId | boolean | Use deterministic device ID generation | true |
| userId | string | Identified user ID to link with sessions | undefined |
| organizationId | string | Organization ID for the identified user | undefined |
| userAttributes | Object | Additional metadata about the identified user | {} |
User Identification
You can identify users to link anonymous sessions with known user accounts:
// Initialize without identification
const sessionable = new Sessionable({
apiKey: "your-api-key",
});
// Start recording
sessionable.start();
// Later, identify the user (e.g., after login)
await sessionable.identify(
"user-123", // User ID (required)
"org-456", // Organization ID (optional)
{
// User attributes (optional)
email: "[email protected]",
plan: "premium",
role: "admin",
}
);You can also provide identification during initialization:
const sessionable = new Sessionable({
apiKey: "your-api-key",
userId: "user-123",
organizationId: "org-456",
userAttributes: {
email: "[email protected]",
plan: "premium",
},
});Anonymous User Identification
By default, Sessionable uses device fingerprinting to generate a stable, deterministic ID for anonymous users. This ensures that the same user gets the same ID across sessions, making it easier to track user behavior before they log in.
// Enable device-based ID generation (default)
const sessionable = new Sessionable({
apiKey: "your-api-key",
useDeviceId: true,
});
// Or disable it to use random IDs
const sessionable = new Sessionable({
apiKey: "your-api-key",
useDeviceId: false,
});The device ID is stored in localStorage as sessionable_anonymous_id.
Framework Integration
Next.js Integration
For Next.js applications, integrate the SessionRecorder component with client-side only rendering:
// pages/_app.tsx or app/layout.tsx
import dynamic from "next/dynamic";
// Client-side only import of SessionRecorder
const SessionRecorderClient = dynamic(
() =>
import("sessionable/react").then((mod) => ({
default: mod.SessionRecorder,
})),
{ ssr: false }
);
function MyApp({ Component, pageProps }) {
return (
<>
<SessionRecorderClient
apiKey={process.env.NEXT_PUBLIC_SESSIONABLE_API_KEY}
autoStart={true}
metadata={{
environment: process.env.NODE_ENV,
deployment: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA,
}}
maskInputs={true}
>
<Component {...pageProps} />
</SessionRecorderClient>
</>
);
}
export default MyApp;This approach ensures:
- The recorder only runs on the client side
- Sessions are captured across all pages in your application
- Recording persists during client-side navigation
Remix Integration
For Remix applications, use the client-only module to ensure server-side rendering compatibility:
// app/root.tsx
import { ClientOnly } from "remix-utils/client-only";
import { SessionRecorder } from "sessionable/react";
export default function App() {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
{/* Other meta tags */}
</head>
<body>
<ClientOnly fallback={null}>
{() => (
<SessionRecorder
apiKey={process.env.SESSIONABLE_API_KEY}
autoStart={true}
maskInputs={true}
>
<Outlet />
</SessionRecorder>
)}
</ClientOnly>
{/* Remix scripts */}
<Scripts />
</body>
</html>
);
}Note: Make sure to include your API key in your environment variables, using the appropriate naming convention for your framework (e.g., NEXT_PUBLIC_ prefix for client-accessible variables in Next.js).
Debug Mode
For local development and testing, you can enable debug mode to use a local API endpoint:
const sessionable = new Sessionable({
apiKey: "your-api-key",
debug: true, // Uses http://localhost:5173 instead of production
});React Hooks API
When using the React integration, you can access the Sessionable instance and metrics using two optimized hooks, which have been designed to prevent unnecessary re-renders in your application:
useSessionable
Access the Sessionable instance, control methods, and recording state. This hook is optimized to prevent re-renders when metrics change (which can happen frequently). It will only trigger re-renders when the recording state changes:
import { useSessionable } from "sessionable/react";
function RecordingControls() {
const {
sessionable, // The Sessionable instance
isRecording, // Current recording state (updates without causing frequent re-renders)
start, // Start recording
stop, // Stop recording
addMetadata, // Add custom metadata
identify, // Identify user
getUserId, // Get identified user ID
getOrganizationId, // Get organization ID
getUserAttributes, // Get user attributes
} = useSessionable();
// This component will only re-render when isRecording changes,
// not when metrics update every second
return (
<div>
<p>Status: {isRecording ? "Recording" : "Not recording"}</p>
<button onClick={() => start()} disabled={isRecording}>
Start Recording
</button>
<button onClick={() => stop()} disabled={!isRecording}>
Stop Recording
</button>
<button
onClick={() => identify("user-123", "org-456")}
disabled={!isRecording}
>
Identify User
</button>
</div>
);
}useSessionableMetrics
Access only the session metrics. Components using this hook will re-render when metrics change (approximately once per second when metrics are actively updating):
import { useSessionableMetrics } from "sessionable/react";
function MetricsDisplay() {
// This component will re-render when metrics change (every ~1 second during recording)
const metrics = useSessionableMetrics();
if (!metrics) return <div>No metrics available</div>;
return (
<div>
<p>Session ID: {metrics.sessionId || "Not recording"}</p>
<p>Events: {metrics.eventsRecorded || 0}</p>
<p>Connection: {metrics.connectionStatus || "disconnected"}</p>
<p>Batches: {metrics.batchesSuccessful}/{metrics.batchesSent}</p>
</div>
);
}Performance Optimized Usage
For optimal performance, follow these patterns:
- Use
useSessionablefor components that need recording controls but don't need to display metrics - Use
useSessionableMetricsonly in components that display metrics data - Memoize components to prevent unnecessary re-renders
import React, { memo } from "react";
import { useSessionable, useSessionableMetrics } from "sessionable/react";
// This component only re-renders when recording state changes
const ControlButtons = memo(() => {
const { start, stop, isRecording } = useSessionable();
return (
<div>
<button onClick={start} disabled={isRecording}>
Start Recording
</button>
<button onClick={stop} disabled={!isRecording}>
Stop Recording
</button>
</div>
);
});
// This component only re-renders when metrics change
const MetricsDisplay = memo(() => {
const metrics = useSessionableMetrics();
if (!metrics) return <div>No metrics available</div>;
return (
<div>
<p>Session ID: {metrics.sessionId || "Not available"}</p>
<p>Events: {metrics.eventsRecorded}</p>
<p>Batches: {metrics.batchesSuccessful}/{metrics.batchesSent}</p>
</div>
);
});
// Main component that includes both control buttons and metrics
function SessionControls() {
return (
<div>
<h3>Session Controls</h3>
<ControlButtons />
<h3>Session Details</h3>
<MetricsDisplay />
</div>
);
}This architecture ensures that:
- The control buttons only re-render when recording starts/stops
- The metrics display only re-renders when metrics change
- Components not directly consuming these hooks won't re-render at all
Examples
Basic Example
Check out the basic example to see a simple HTML implementation.
SaaS Demo
For a complete React application example, see the SaaS Demo which shows a healthcare platform with the SDK fully integrated.
Advanced Usage
Text Masking with CSS Selectors
You can protect sensitive information on your page by specifying a CSS selector for text that should be masked:
const sessionable = new Sessionable({
apiKey: "your-api-key",
maskTextSelector: ".sensitive-data, [data-mask], .user-pii",
});With this configuration, any element matching these selectors will have its text content masked in recordings:
<!-- These texts will be masked in recordings -->
<span class="sensitive-data">Account: 1234-5678-9012</span>
<div data-mask>SSN: 123-45-6789</div>
<p class="user-pii">Jane Smith</p>Adding Custom Metadata
// During initialization
const sessionable = new Sessionable({
apiKey: "your-api-key",
metadata: {
userId: "123",
userType: "premium",
environment: "production",
},
});
// Or later during recording
sessionable.addMetadata({
currentPage: window.location.pathname,
referrer: document.referrer,
timestamp: new Date().toISOString(),
});Getting Session Metrics
You can retrieve session metrics at any time using the getMetrics method:
const metrics = sessionable.getMetrics();
console.log("Session ID:", metrics.sessionId);
console.log("Events recorded:", metrics.eventsRecorded);
console.log("Connection status:", metrics.connectionStatus);TypeScript Support
The package includes TypeScript definitions for all public APIs.
License
All rights reserved. © Sessionable
