0.5.0 • Published 3 months ago

sessionable v0.5.0

Weekly downloads
-
License
-
Repository
-
Last release
3 months ago

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:

OptionTypeDescriptionDefault
apiKeystringYour Sessionable API keyRequired
autoStartbooleanStart recording automaticallyfalse
metadataObjectCustom metadata to include{}
maskInputsbooleanWhether to mask input fields (passwords, text, etc.)false
maskTextSelectorstringCSS selector for text elements that should be maskedundefined
debugbooleanEnable debug mode (uses localhost:5173 endpoint)false
useDeviceIdbooleanUse deterministic device ID generationtrue
userIdstringIdentified user ID to link with sessionsundefined
organizationIdstringOrganization ID for the identified userundefined
userAttributesObjectAdditional 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

0.5.0

3 months ago

0.4.10

3 months ago

0.4.9

3 months ago

0.4.8

3 months ago

0.4.7

3 months ago

0.4.6

3 months ago

0.4.5

3 months ago

0.4.4

3 months ago

0.4.3

3 months ago

0.4.2

3 months ago

0.4.1

3 months ago

0.4.0

3 months ago

0.3.5

3 months ago

0.3.4

3 months ago

0.3.3

3 months ago

0.3.2

3 months ago

0.3.1

3 months ago

0.3.0

3 months ago

0.2.3

3 months ago

0.2.2

3 months ago

0.2.0

3 months ago

0.2.1

3 months ago

0.1.1

4 months ago

0.1.0

4 months ago