npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

sessionable

v0.5.0

Published

Session recording SDK for Sessionable

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 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: "[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:

  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