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.1

Published

Smart React error boundaries with recovery, observability, and DX

Readme

react-rescuer

Smart React error boundaries with recovery, observability, and DX.

npm CI License: MIT

  • Repository: https://github.com/rody-huancas/react-rescuer
  • Author: https://github.com/rody-huancas
  • Star the repo: https://github.com/rody-huancas/react-rescuer/stargazers

What you get

  • Catch render-time errors with a familiar ErrorBoundary
  • Reset patterns: resetError() + resetKeys
  • Optional retries with delays/backoff (recovery)
  • Structured ErrorContext for fallbacks (fingerprint, breadcrumbs, component stack, session id)
  • Optional observability helpers (breadcrumbs + fingerprint + buildErrorContext)
  • Development overlay in dev, your fallback in prod

Install

npm

npm i react-rescuer

pnpm

pnpm add react-rescuer

yarn

yarn add react-rescuer

bun

bun add react-rescuer

Peer deps:

  • react >= 18
  • react-dom >= 18

30-second setup

  1. Wrap a page/route/section with ErrorBoundary
import { ErrorBoundary } from "react-rescuer";

export function App() {
  return (
    <ErrorBoundary fallback={<div>Something went wrong.</div>}>
      <Page />
    </ErrorBoundary>
  );
}
  1. Use a real fallback UI and call resetError()
import { ErrorBoundary } from "react-rescuer";
import type { FallbackProps } from "react-rescuer";

function Fallback({ error, resetError }: FallbackProps) {
  return (
    <div role="alert">
      <div>Oops:</div>
      <pre>{error.message}</pre>
      <button type="button" onClick={resetError}>
        Try again
      </button>
    </div>
  );
}

export function App() {
  return (
    <ErrorBoundary FallbackComponent={Fallback}>
      <Page />
    </ErrorBoundary>
  );
}
  1. Report errors (optional)
import { ErrorBoundary } from "react-rescuer";

export function App() {
  return (
    <ErrorBoundary
      onError={(error, _info, ctx) => {
        // send to Sentry, Datadog, Logtail, your API, etc.
        console.log("caught", { error, ctx });
      }}
      fallback={<div>Something went wrong.</div>}
    >
      <Page />
    </ErrorBoundary>
  );
}

Common recipes

Automatic reset with resetKeys:

import { useState } from "react";
import { ErrorBoundary } from "react-rescuer";

function Bomb({ armed }: { armed: boolean }) {
  if (armed) throw new Error("boom");
  return <div>OK</div>;
}

export function Demo() {
  const [armed, setArmed] = useState(false);

  return (
    <div>
      <button type="button" onClick={() => setArmed(true)}>
        Throw
      </button>
      <button type="button" onClick={() => setArmed(false)}>
        Reset
      </button>

      <ErrorBoundary resetKeys={[armed]} fallback={<div>Fallback</div>}>
        <Bomb armed={armed} />
      </ErrorBoundary>
    </div>
  );
}

Errors from events / async code (use useErrorBoundary()):

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

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

  return (
    <button
      type="button"
      onClick={async () => {
        try {
          await fetch("/api/save");
          throw new Error("simulate async error");
        } catch (e) {
          showBoundary(e as Error);
        }
      }}
    >
      Save
    </button>
  );
}

export function Demo() {
  return (
    <ErrorBoundary fallback={<div>Fallback</div>}>
      <SaveButton />
    </ErrorBoundary>
  );
}

Retries with recovery:

import { ErrorBoundary } from "react-rescuer";

export function Demo() {
  return (
    <ErrorBoundary
      recovery={{
        maxRetries: 3,
        retryDelay: (attempt) => Math.min(1000, 200 * 2 ** (attempt - 1)),
      }}
      fallbackRender={({ error, resetError, retryCount }) => (
        <div>
          <div>{error.message}</div>
          <div>retryCount: {retryCount}</div>
          <button type="button" onClick={resetError}>
            Retry
          </button>
        </div>
      )}
    >
      <Page />
    </ErrorBoundary>
  );
}

API

ErrorBoundary props (high-signal)

type ErrorBoundaryProps<E extends Error = Error> = {
  children: React.ReactNode;

  // fallback UI (pick one)
  fallback?: React.ReactNode;
  fallbackRender?: (props: FallbackProps<E>) => React.ReactNode;
  FallbackComponent?: React.ComponentType<FallbackProps<E>>;

  // reset behavior
  resetKeys?: unknown[];
  onReset?: (details: { reason: "imperative" | "resetKeys" | "retry" }) => void;

  // reporting
  onError?: (error: E, errorInfo: React.ErrorInfo, errorContext: ErrorContext) => void;

  // retries (optional)
  recovery?: {
    maxRetries: number;
    retryDelay?: number | ((attempt: number, error: E) => number);
    isRecoverable?: (error: E) => boolean;
    onMaxRetriesReached?: (error: E, context: ErrorContext) => void;
  };

  // observability (optional)
  getBreadcrumbs?: () => Breadcrumb[];
  fingerprint?: (error: E) => string;
  contextBuilder?: (
    error: E,
    errorInfo: React.ErrorInfo,
    options: {
      sessionId: string;
      errorCount: number;
      retryCount: number;
      boundaryProps?: unknown;
      getBreadcrumbs?: () => Breadcrumb[];
      fingerprint?: (error: E) => string;
    },
  ) => ErrorContext;
};

Fallback props

type FallbackProps<E extends Error = Error> = {
  error: E;
  errorContext: ErrorContext;
  resetError: () => void;
  retryCount: number;
};

ErrorContext fields

type ErrorContext = {
  error: Error;
  fingerprint: string;
  breadcrumbs: Breadcrumb[];
  componentStack: string;
  timestamp: number;
  sessionId: string;
  errorCount: number;
  boundaryProps?: unknown;
};

Observability (optional)

Recommended: inject buildErrorContext and use breadcrumbs.

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

export function Demo() {
  return (
    <ErrorBoundary
      contextBuilder={buildErrorContext}
      onError={(error, _info, ctx) => {
        console.log("ErrorContext", { error, ctx });
      }}
      fallback={<div>Check the console</div>}
    >
      <button
        type="button"
        onClick={() => addBreadcrumb({ type: "custom", message: "user clicked" })}
      >
        Add breadcrumb
      </button>
    </ErrorBoundary>
  );
}

Breadcrumbs clear on reset via a DOM event: react-rescuer:reset.

Development overlay

In development (process.env.NODE_ENV === "development"), ErrorBoundary attempts to render a built-in overlay (via a conditional require() import). In production it renders your configured fallback.

Testing

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

installMatchers();

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

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

// expect(testBoundary).toHaveCaughtError();
// expect(getLastContext()?.fingerprint).toBeTruthy();

Import paths

import { ErrorBoundary } from "react-rescuer";

import { useErrorBoundary, useErrorContext } from "react-rescuer/hooks";
import { withErrorBoundary } from "react-rescuer/hoc";

import {
  addBreadcrumb,
  BreadcrumbTrail,
  buildErrorContext,
  fingerprintError,
  getBreadcrumbTrail,
} from "react-rescuer/observability";

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

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

License

MIT