sessionable
TypeScript icon, indicating that this package has built-in type declarations

0.5.0 • Public • Published

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 sessionable

Quick 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: "user@example.com",
    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: "user@example.com",
    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:

  1. Use useSessionable for components that need recording controls but don't need to display metrics
  2. Use useSessionableMetrics only in components that display metrics data
  3. 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

Package Sidebar

Install

npm i sessionable

Weekly Downloads

3

Version

0.5.0

License

none

Unpacked Size

9.78 MB

Total Files

312

Last publish

Collaborators

  • jacobpedd