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 🙏

© 2026 – Pkg Stats / Ryan Hefner

errormirror

v1.0.0

Published

Record and replay frontend errors for debugging without external services

Readme

ErrorMirror

A lightweight JavaScript/TypeScript library that records frontend errors and replays them later for debugging without using any external services. ErrorMirror captures minimal snapshots of your application state when errors occur, allowing you to reproduce and debug issues in a sandboxed environment.

Features

  • 🎯 Automatic Error Recording - Hooks into window.onerror and unhandledrejection events
  • 📸 Minimal Snapshots - Captures DOM structure, user events, fetch calls, and framework state
  • 🔄 Error Replay - Replays errors in a sandboxed iframe for safe debugging
  • 💾 Local Storage - Stores snapshots locally using localStorage (custom storage engines supported)
  • 🎨 Framework Support - Detects and captures state from React, Vue, and Svelte
  • 📦 Tree Shakeable - Fully tree-shakeable for optimal bundle size
  • 🔧 TypeScript - Full TypeScript support with exported types
  • 🚀 Zero Dependencies - No external dependencies required

Installation

npm install errormirror

Quick Start

Basic Usage

import { recordErrors, replayError } from 'errormirror';

// Start recording errors
const stopRecording = recordErrors();

// When an error occurs, it's automatically captured
// ... your app code ...

// Stop recording
stopRecording();

// Replay a captured error
await replayError('err_1234567890_abc123');

With Custom Storage

import { recordErrors, replayError, LocalStorageEngine } from 'errormirror';

// Create custom storage engine
class CustomStorage implements StorageEngine {
  save(id: string, snapshot: ErrorSnapshot): void {
    // Your custom save logic
  }
  
  load(id: string): ErrorSnapshot | null {
    // Your custom load logic
  }
  
  list(): string[] {
    // Your custom list logic
  }
  
  remove(id: string): void {
    // Your custom remove logic
  }
}

const storage = new CustomStorage();

// Use custom storage
const stopRecording = recordErrors({ storage });

API Reference

recordErrors(options?: RecordOptions): () => void

Starts recording errors and returns a cleanup function to stop recording.

Options

interface RecordOptions {
  storage?: StorageEngine;              // Custom storage engine (default: LocalStorageEngine)
  maxEvents?: number;                   // Maximum events to track (default: 50)
  maxFetchCalls?: number;               // Maximum fetch calls to track (default: 20)
  captureDOM?: boolean;                 // Capture DOM snapshot (default: true)
  captureEvents?: boolean;              // Capture user events (default: true)
  captureFetch?: boolean;               // Capture fetch calls (default: true)
  captureFrameworkState?: boolean;      // Capture framework state (default: true)
  frameworks?: ('react' | 'vue' | 'svelte')[]; // Frameworks to detect (default: all)
}

Example

const stopRecording = recordErrors({
  maxEvents: 100,
  captureDOM: true,
  captureEvents: true,
  captureFetch: true,
  captureFrameworkState: true
});

// Later...
stopRecording();

replayError(snapshotId: string, options?: ReplayOptions): Promise<void>

Replays a captured error in a sandboxed iframe.

Options

interface ReplayOptions {
  storage?: StorageEngine;              // Custom storage engine (default: LocalStorageEngine)
  iframeId?: string;                    // ID for the replay iframe (default: 'errormirror-replay')
  onComplete?: (error: Error | null) => void; // Callback when replay completes
}

Example

await replayError('err_1234567890_abc123', {
  onComplete: (error) => {
    if (error) {
      console.error('Error replayed:', error);
    } else {
      console.log('Replay completed successfully');
    }
  }
});

Error Snapshot Structure

Each captured error snapshot contains:

interface ErrorSnapshot {
  id: string;                    // Unique snapshot ID
  timestamp: number;             // Timestamp when error occurred
  error: {
    message: string;             // Error message
    stack?: string;              // Error stack trace
    filename?: string;           // File where error occurred
    lineno?: number;             // Line number
    colno?: number;              // Column number
    type: 'error' | 'unhandledrejection';
    reason?: any;                // Rejection reason (for promise rejections)
  };
  dom: {
    html: string;                // Minimal HTML structure
    bodyAttributes: Record<string, string>;
    viewport: { width: number; height: number };
  };
  events: UserEvent[];           // Recent user events
  fetchCalls: FetchCall[];      // Recent fetch API calls
  frameworkState?: FrameworkState; // Framework state (if detected)
}

Storage Engines

LocalStorageEngine (Default)

Uses browser localStorage to store snapshots. Perfect for development and testing.

import { LocalStorageEngine } from 'errormirror';

const storage = new LocalStorageEngine();

Custom Storage Engine

Implement the StorageEngine interface to use your own storage:

import type { StorageEngine, ErrorSnapshot } from 'errormirror';

class MyStorage implements StorageEngine {
  async save(id: string, snapshot: ErrorSnapshot): Promise<void> {
    // Save to your backend, IndexedDB, etc.
  }
  
  async load(id: string): Promise<ErrorSnapshot | null> {
    // Load from your storage
  }
  
  async list(): Promise<string[]> {
    // List all snapshot IDs
  }
  
  async remove(id: string): Promise<void> {
    // Remove a snapshot
  }
}

Framework Support

ErrorMirror automatically detects and captures state from:

  • React - Detects React components and state via React internals
  • Vue - Detects Vue instances via Vue DevTools hooks
  • Svelte - Detects Svelte components via DOM markers

Framework detection is optional and can be configured:

recordErrors({
  captureFrameworkState: true,
  frameworks: ['react'] // Only detect React
});

Demo

See the demo/ folder for a complete React example showing how to use ErrorMirror.

To run the demo:

cd demo
npm install
npm run dev

Development

Building

npm run build

Testing

npm test

Project Structure

errormirror/
├── src/
│   ├── index.ts           # Main exports
│   ├── types.ts           # TypeScript definitions
│   ├── storage.ts         # Storage engines
│   ├── dom-capture.ts     # DOM snapshot capture
│   ├── event-tracker.ts   # User event tracking
│   ├── fetch-tracker.ts   # Fetch API tracking
│   ├── framework-detector.ts # Framework state detection
│   ├── compression.ts     # Compression utilities
│   ├── recorder.ts        # Error recording logic
│   └── replayer.ts        # Error replay logic
├── tests/                 # Test files
├── demo/                  # Demo React app
└── dist/                  # Built files

Best Practices

  1. Start Recording Early - Start recording as early as possible in your app lifecycle
  2. Limit Event History - Use maxEvents and maxFetchCalls to control memory usage
  3. Custom Storage - For production, consider using a custom storage engine that sends snapshots to your backend
  4. Selective Capture - Disable unnecessary captures (e.g., captureFetch: false) if you don't need them
  5. Clean Up - Always call the cleanup function when stopping recording

Limitations

  • DOM snapshots are minimal and may not capture all dynamic content
  • Framework state capture depends on framework internals and may not work in all scenarios
  • Replay accuracy depends on the completeness of captured data
  • Large applications may generate large snapshots

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.