Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@sjforge/feedback-widget

Package Overview
Dependencies
Maintainers
1
Versions
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sjforge/feedback-widget

In-app feedback widget SDK with screenshots, annotations, and session recording

latest
Source
npmnpm
Version
0.3.0
Version published
Weekly downloads
1
Maintainers
1
Weekly downloads
 
Created
Source

@sjforge/feedback-widget

In-app feedback widget SDK with screenshots, annotations, session recording, and offline support.

Features

  • Screenshot capture with annotation tools (draw, arrows, text, highlight, blur)
  • Session recording using rrweb for pixel-perfect replay
  • Automatic context capture (browser, errors, network failures)
  • Offline support with IndexedDB queue and automatic sync
  • Privacy controls for masking sensitive data
  • Multiple platforms - Web, Electron, and React Native

Installation

npm install @sjforge/feedback-widget

Or via CDN:

<script src="https://cdn.sjforge.dev/feedback-widget.min.js"></script>

Quick Start

Web / Vanilla JS

import { FeedbackWidget } from '@sjforge/feedback-widget';

// Initialize the widget
FeedbackWidget.init({
  projectId: 'my-project',
  apiKey: 'fpk_xxxxx', // Get from the admin panel
});

// The floating button appears automatically
// Or submit programmatically:
await FeedbackWidget.submit({
  type: 'bug',
  priority: 'high',
  title: 'Button not working',
  description: 'The submit button does nothing when clicked',
});

CDN / Script Tag

<script src="https://cdn.sjforge.dev/feedback-widget.min.js"></script>
<script>
  FeedbackWidget.init({
    projectId: 'my-project',
    apiKey: 'fpk_xxxxx',
  });
</script>

Electron

import { FeedbackWidget, ElectronAdapter } from '@sjforge/feedback-widget';

FeedbackWidget.init({
  projectId: 'my-app',
  apiKey: 'fpk_xxxxx',
  adapter: new ElectronAdapter(window.api),
});

See Electron Integration Guide for preload script setup.

React Native

Use the separate native package:

npm install @sjforge/feedback-widget-native
import { FeedbackWidget } from '@sjforge/feedback-widget-native';

FeedbackWidget.init({
  projectId: 'my-app',
  apiKey: 'fpk_xxxxx',
});

See @sjforge/feedback-widget-native for full documentation.

Configuration

FeedbackWidget.init({
  // Required
  projectId: 'my-project',
  apiKey: 'fpk_xxxxx',

  // Optional API endpoint (defaults to https://feedback.sjforge.dev/api/widget)
  apiUrl: 'https://your-portal.com/api/widget',

  // Optional user info (attached to all submissions)
  user: {
    name: 'John Doe',
    email: 'john@example.com',
  },

  // Custom context (sent with every submission)
  customContext: {
    subscription: 'premium',
    version: '2.1.0',
  },

  // Privacy settings
  privacy: {
    maskSelectors: ['.sensitive-data', '.credit-card'],
    blockSelectors: ['.do-not-record'],
    autoMaskPasswords: true, // default: true
  },

  // UI customization
  ui: {
    position: 'bottom-right', // 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'
    primaryColor: '#007bff',
    showButton: true,
    buttonText: 'Feedback',
  },

  // Feature flags
  features: {
    screenshots: true,          // Enable screenshot capture
    recording: false,           // Enable session recording
    captureConsoleErrors: true, // Capture console.error calls
    captureNetworkErrors: true, // Capture failed fetch/XHR requests
  },

  // Recording options (when features.recording = true)
  recording: {
    maxDuration: 300000,     // Max recording duration in ms (default: 5 min)
    samplingRate: 'medium',  // 'low' | 'medium' | 'high'
  },

  // Callbacks
  onSubmitStart: () => console.log('Submitting...'),
  onSubmitSuccess: (feedbackId) => console.log('Submitted:', feedbackId),
  onSubmitError: (error) => console.error('Error:', error),
});

API Reference

Static Methods

FeedbackWidget.init(config)

Initialize the widget. Must be called before using other methods.

FeedbackWidget.submit(feedback)

Submit feedback programmatically.

const result = await FeedbackWidget.submit({
  type: 'bug',       // 'bug' | 'feature' | 'design'
  priority: 'high',  // 'low' | 'medium' | 'high' | 'critical'
  title: 'Short title',
  description: 'Detailed description',
});

if (result.success) {
  console.log('Feedback ID:', result.feedback_id);
} else {
  console.error('Error:', result.error);
}

FeedbackWidget.open() / FeedbackWidget.close()

Open or close the feedback form UI.

FeedbackWidget.setContext(context)

Update custom context that gets sent with submissions.

FeedbackWidget.setContext({
  userId: 'user-123',
  page: 'checkout',
});

FeedbackWidget.getContext()

Get the current context snapshot (for debugging).

FeedbackWidget.destroy()

Destroy the widget instance and clean up resources.

FeedbackWidget.isInitialized()

Returns true if the widget is initialized.

FeedbackWidget.getVersion()

Get the SDK version string.

Screenshot Methods

FeedbackWidget.captureScreenshot()

Capture a screenshot of the current page.

const screenshot = await FeedbackWidget.captureScreenshot();
// Returns { dataUrl: string, width: number, height: number }

FeedbackWidget.submitWithScreenshot(feedback)

Submit feedback with an automatically captured screenshot.

await FeedbackWidget.submitWithScreenshot({
  type: 'bug',
  priority: 'high',
  title: 'UI Issue',
  description: 'See attached screenshot',
});

Recording Methods

FeedbackWidget.startRecording()

Start a session recording.

FeedbackWidget.startRecording();

FeedbackWidget.stopRecording()

Stop the current recording and return the data.

const recording = await FeedbackWidget.stopRecording();
// Returns { events: Event[], duration: number }

FeedbackWidget.isRecording()

Check if a recording is in progress.

FeedbackWidget.submitWithRecording(feedback)

Submit feedback with the current recording attached.

await FeedbackWidget.submitWithRecording({
  type: 'bug',
  priority: 'critical',
  title: 'Workflow broken',
  description: 'See attached recording',
});

Offline Methods

FeedbackWidget.isOnline()

Check if the widget has network connectivity.

FeedbackWidget.getPendingCount()

Get the number of submissions waiting to sync.

const pending = await FeedbackWidget.getPendingCount();
console.log(`${pending} submissions waiting to sync`);

FeedbackWidget.syncOffline()

Force sync offline submissions.

const { succeeded, failed } = await FeedbackWidget.syncOffline();

Screenshot & Annotation

Automatic Screenshot Capture

Screenshots are captured using html2canvas and can include annotations.

// Capture screenshot programmatically
const screenshot = await FeedbackWidget.captureScreenshot();

// Submit with screenshot
await FeedbackWidget.submitWithScreenshot({
  type: 'bug',
  priority: 'high',
  title: 'UI Issue',
  description: 'See attached screenshot',
});

Annotation Editor

The SDK includes a standalone annotation editor:

import { AnnotationEditor } from '@sjforge/feedback-widget';

const editor = new AnnotationEditor({
  container: document.getElementById('editor'),
  imageSrc: screenshotDataUrl,
  onSave: (annotatedImageDataUrl) => {
    console.log('Annotated image:', annotatedImageDataUrl);
  },
  onCancel: () => {
    console.log('Cancelled');
  },
});

// Get annotated image
const annotatedImage = editor.getAnnotatedImage();

// Clean up
editor.destroy();

Available annotation tools:

  • Rectangle - Draw boxes around areas
  • Arrow - Point to specific elements
  • Text - Add text labels
  • Highlight - Semi-transparent highlight
  • Blur - Obscure sensitive areas

Session Recording

Session recording captures DOM events using rrweb for pixel-perfect replay in the admin portal.

Enable Recording

FeedbackWidget.init({
  projectId: 'my-project',
  apiKey: 'fpk_xxxxx',
  features: {
    recording: true,
  },
  recording: {
    maxDuration: 300000, // 5 minutes max
  },
});

Manual Recording Control

// Start recording
FeedbackWidget.startRecording();

// Check status
if (FeedbackWidget.isRecording()) {
  console.log('Recording in progress...');
}

// Stop and submit
await FeedbackWidget.submitWithRecording({
  type: 'bug',
  priority: 'high',
  title: 'See what happened',
  description: 'Recording attached',
});

Recording Privacy

Recordings automatically respect privacy settings:

FeedbackWidget.init({
  privacy: {
    maskSelectors: ['.sensitive'],     // Masked in recordings
    blockSelectors: ['.private'],       // Excluded from recordings
    autoMaskPasswords: true,            // Password inputs masked
  },
});

Privacy & Security

Automatic Protection

  • Password fields are automatically masked
  • Recordings exclude blocked elements entirely
  • Screenshots mask sensitive areas

Data Attributes

<!-- Mask this element's content -->
<div data-feedback-mask>Sensitive content</div>

<!-- Completely exclude from capture -->
<div data-feedback-block>Private notes</div>

Configuration

FeedbackWidget.init({
  privacy: {
    // CSS selectors to mask (shown as solid blocks)
    maskSelectors: ['.credit-card', '.ssn-field'],
    // CSS selectors to completely exclude
    blockSelectors: ['.private-notes', '.admin-panel'],
    // Auto-mask password fields (default: true)
    autoMaskPasswords: true,
  },
});

Offline Support

The widget automatically queues submissions when offline and syncs when connectivity returns.

How It Works

  • When offline, submissions are stored in IndexedDB
  • When connectivity returns, queued items sync automatically
  • Failed syncs retry up to 3 times with exponential backoff
  • Callbacks notify you of sync status

Manual Control

// Check online status
if (!FeedbackWidget.isOnline()) {
  console.log('Currently offline - submissions will queue');
}

// Check pending count
const pending = await FeedbackWidget.getPendingCount();

// Force sync attempt
const { succeeded, failed } = await FeedbackWidget.syncOffline();
console.log(`Synced: ${succeeded}, Failed: ${failed}`);

Auto-Captured Context

The widget automatically captures:

ContextDescription
User AgentBrowser and OS information
ViewportCurrent window dimensions
ScreenDevice screen size and pixel ratio
URLCurrent page URL
ReferrerHow user arrived at the page
Console ErrorsRecent console.error calls
Network ErrorsFailed fetch/XHR requests
Custom ContextData you provide via setContext()

Electron Integration

1. Install the Package

npm install @sjforge/feedback-widget

2. Set Up Preload Script

// preload.ts
import { contextBridge, ipcRenderer } from 'electron';

contextBridge.exposeInMainWorld('feedbackAPI', {
  // Screenshot capture
  captureScreen: () => ipcRenderer.invoke('feedback:capture-screen'),

  // Offline storage
  storeOffline: (key: string, data: unknown) =>
    ipcRenderer.invoke('feedback:store-offline', key, data),
  getOffline: (key: string) =>
    ipcRenderer.invoke('feedback:get-offline', key),
  removeOffline: (key: string) =>
    ipcRenderer.invoke('feedback:remove-offline', key),

  // App info
  getAppInfo: () => ipcRenderer.invoke('feedback:get-app-info'),
});

3. Set Up Main Process Handlers

// main.ts
import { ipcMain, desktopCapturer, app } from 'electron';
import Store from 'electron-store';

const store = new Store({ name: 'feedback-widget' });

ipcMain.handle('feedback:capture-screen', async () => {
  const sources = await desktopCapturer.getSources({
    types: ['window'],
    thumbnailSize: { width: 1920, height: 1080 },
  });
  const currentWindow = sources.find(s => s.name === 'Your App Name');
  return currentWindow?.thumbnail.toDataURL();
});

ipcMain.handle('feedback:store-offline', (_, key, data) => {
  store.set(key, data);
});

ipcMain.handle('feedback:get-offline', (_, key) => {
  return store.get(key);
});

ipcMain.handle('feedback:remove-offline', (_, key) => {
  store.delete(key);
});

ipcMain.handle('feedback:get-app-info', () => ({
  name: app.getName(),
  version: app.getVersion(),
}));

4. Initialize in Renderer

// renderer.ts
import { FeedbackWidget, ElectronAdapter } from '@sjforge/feedback-widget';

FeedbackWidget.init({
  projectId: 'my-electron-app',
  apiKey: 'fpk_xxxxx',
  adapter: new ElectronAdapter(window.feedbackAPI),
});

CDN Usage

For quick integration without a build step:

<!DOCTYPE html>
<html>
<head>
  <title>My App</title>
</head>
<body>
  <!-- Your app content -->

  <script src="https://cdn.sjforge.dev/feedback-widget.min.js"></script>
  <script>
    FeedbackWidget.init({
      projectId: 'my-project',
      apiKey: 'fpk_xxxxx',
      ui: {
        position: 'bottom-right',
        primaryColor: '#007bff',
      },
    });
  </script>
</body>
</html>

TypeScript Support

Full TypeScript definitions are included:

import {
  FeedbackWidget,
  WidgetConfig,
  FeedbackSubmission,
  SubmissionResponse,
  FeedbackType,
  FeedbackPriority,
} from '@sjforge/feedback-widget';

const config: WidgetConfig = {
  projectId: 'my-project',
  apiKey: 'fpk_xxxxx',
};

FeedbackWidget.init(config);

Browser Support

  • Chrome 80+
  • Firefox 75+
  • Safari 13+
  • Edge 80+

Bundle Size

ImportSize (gzipped)
Core only~15 KB
+ Screenshots~45 KB
+ Recording~85 KB

Changelog

0.3.0

  • Session recording with rrweb
  • Chunked upload for large recordings
  • Recording playback in admin portal

0.2.0

  • Screenshot capture with html2canvas
  • Annotation editor with drawing tools
  • Privacy masking for screenshots

0.1.0

  • Initial release
  • Text-only feedback submission
  • Auto-captured context
  • Offline queue with sync

License

MIT

Keywords

feedback

FAQs

Package last updated on 30 Jan 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts