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

react-rescuer

v0.1.4

Published

React error boundaries with automatic recovery, observability, breadcrumbs, and DevOverlay

Readme

react-rescuer

Error boundaries for React 18 — with automatic recovery, observability, and zero config DevOverlay.

npm CI License: MIT

Why react-rescuer?

React's built-in error boundaries catch render errors — and that's it. react-rescuer adds automatic retry with backoff, structured observability (breadcrumbs, fingerprinting, session tracking), a zero-config DevOverlay in development, and testing utilities. All opt-in, zero config to start.

Install

npm i react-rescuer
# pnpm add react-rescuer  |  yarn add react-rescuer  |  bun add react-rescuer

Peer deps: react >= 18, react-dom >= 18

Quick start

import { ErrorBoundary } from "react-rescuer";

export function App() {
  return (
    <ErrorBoundary fallback={<p>Something went wrong.</p>}>
      <Page />
    </ErrorBoundary>
  );
}

That's it for the happy path. Everything below is opt-in.

Features

Fallback UI

Three modes — pick the one that fits:

// static node
<ErrorBoundary fallback={<p>Error</p>}>

// render prop — access error + reset
<ErrorBoundary fallbackRender={({ error, resetError }) => (
  <div>
    <p>{error.message}</p>
    <button onClick={resetError}>Retry</button>
  </div>
)}>

// component
<ErrorBoundary FallbackComponent={MyFallback}>

FallbackProps received by fallbackRender / FallbackComponent:

type FallbackProps = {
  error: Error;
  errorContext: ErrorContext; // fingerprint, breadcrumbs, sessionId, …
  resetError: () => void;
  retryCount: number;
};

Automatic recovery

Pass a recovery prop to retry automatically with exponential backoff:

import { ErrorBoundary } from "react-rescuer";

<ErrorBoundary
  recovery={{
    maxRetries: 3,
    retryDelay: (attempt) => Math.min(8000, 250 * 2 ** (attempt - 1)), // 250 → 500 → 1000 …
    isRecoverable: (error) => error.name !== "FatalError",
    onMaxRetriesReached: (error, ctx) => reportToSentry(error, ctx),
  }}
  fallbackRender={({ error, retryCount }) => (
    <p>
      {error.message} — attempt {retryCount}
    </p>
  )}
>
  <DataWidget />
</ErrorBoundary>;

For orchestrating retries across multiple boundaries, use RetryManager from react-rescuer/recovery:

import { RetryManager, createExponentialBackoff } from "react-rescuer/recovery";

const manager = new RetryManager(
  { maxRetries: 5 },
  createExponentialBackoff(250, 10_000),
);

const { ok, delayMs } = manager.next("widget-boundary", error, context);

Observability

Every error gives you a structured ErrorContext out of the box:

type ErrorContext = {
  error: Error;
  fingerprint: string; // stable hash across deploys
  breadcrumbs: Breadcrumb[]; // last 20 user actions before the crash
  componentStack: string;
  sessionId: string;
  errorCount: number;
  timestamp: number;
};

Auto-capture clicks and navigation events, then attach them to the boundary:

import { ErrorBoundary } from "react-rescuer";
import { addBreadcrumb, buildErrorContext } from "react-rescuer/observability";

// manually add a breadcrumb anywhere in your app
addBreadcrumb({ type: "custom", message: "user submitted form" });

<ErrorBoundary
  contextBuilder={buildErrorContext}
  onError={(error, _info, ctx) => {
    sendToMonitoring({ error, ctx }); // fingerprint + breadcrumbs included
  }}
  fallback={<p>Something went wrong.</p>}
>
  <CheckoutForm />
</ErrorBoundary>;

getBreadcrumbTrail() auto-starts on first call and captures click, pushState, replaceState, and popstate. Breadcrumbs clear on boundary reset.

DevOverlay

In development, ErrorBoundary automatically renders a built-in overlay with the error, stack, component tree, retries left, and breadcrumbs. No setup needed — it tree-shakes to zero in production.

Async errors

React's componentDidCatch only catches render-time errors. Use useErrorBoundary to route async or event-handler errors into the nearest boundary:

import { ErrorBoundary } from "react-rescuer";
import { useErrorBoundary } from "react-rescuer/hooks";

function SaveButton() {
  const { showBoundary } = useErrorBoundary();

  return (
    <button
      onClick={async () => {
        try {
          await api.save();
        } catch (e) {
          showBoundary(e as Error);
        }
      }}
    >
      Save
    </button>
  );
}

<ErrorBoundary fallback={<p>Save failed.</p>}>
  <SaveButton />
</ErrorBoundary>;

HOC

import { withErrorBoundary } from "react-rescuer/hoc";

const SafeWidget = withErrorBoundary(Widget, {
  fallback: <p>Widget failed to load.</p>,
});

Testing

import { render } from "@testing-library/react";
import { createTestBoundary, installMatchers } from "react-rescuer/testing";

installMatchers(); // adds toHaveCaughtError + toHaveCaughtErrorMatching to expect

const tb = createTestBoundary();
const { Boundary, getLastContext } = tb;

function Bomb() {
  throw new Error("boom");
  return null;
}

render(
  <Boundary>
    <Bomb />
  </Boundary>,
);

expect(tb).toHaveCaughtError();
expect(tb).toHaveCaughtErrorMatching("boom");
expect(getLastContext()?.fingerprint).toBeTruthy();

Import paths

import { ErrorBoundary } from "react-rescuer";
import { withErrorBoundary } from "react-rescuer/hoc";
import { useErrorBoundary, useErrorContext } from "react-rescuer/hooks";
import { createTestBoundary, installMatchers } from "react-rescuer/testing";
import { RetryManager, createExponentialBackoff } from "react-rescuer/recovery";
import { addBreadcrumb, buildErrorContext, fingerprintError, getBreadcrumbTrail } from "react-rescuer/observability";

Contributing

Issues and PRs welcome. See the repository for setup instructions.

License

MIT © Rody Huancas