mrkeyoor.com_
Thu 06 Aug 10:54 UTC
npmWeb Frontendupdated 06 Aug 2026

react-error-boundary

React only lets class components catch render errors, and writing that class yourself gets you a boundary that can show a fallback but cannot easily be reset, cannot be triggered from an event handler, and has no hook API. react-error-boundary is that class component written once, properly, plus the pieces around it: three ways to declare a fallback, a resetErrorBoundary callback handed to the fallback so the user can retry, a resetKeys prop that clears the error automatically when some piece of state changes, and a useErrorBoundary hook whose showBoundary lets you push an error from a click handler or an async callback into the nearest boundary. It has no runtime dependencies, weighs under a kilobyte gzipped, and works with any React renderer including React Native.

Verdict

The default answer for error boundaries in React, small enough that the decision is not worth agonising over, and the reset and showBoundary APIs are the parts you would otherwise write badly yourself. Just go in knowing it catches render errors only, which is a React rule and not something this package can change.

API stability4/5ErrorBoundary, its three fallback props, and useErrorBoundary have been stable since version 4. The majors do move though: version 6 switched to ES modules, and the onReset callback now receives a details object with a reason field instead of the loose arguments earlier versions passed, so an upgrade is a real read of the release notes rather than a bump.
Docs4/5There is a dedicated documentation site with an API reference, a common-questions page, and a README that spells out exactly which errors React boundaries do and do not catch, which is the single most misunderstood thing about them. What is missing is depth on the awkward cases, such as boundaries under Suspense or how this interacts with framework-level error pages.
Maintenance4/5Brian Vaughn maintains it and the tracker is at zero open issues and PRs, which is unusual and reflects a tight scope rather than neglect. The repository was last pushed on 2026-07-12 and 6.1.2 shipped in May 2026. It is effectively one person's project, so the bus factor is the risk, not responsiveness.
Ecosystem5/5Around 14.7M downloads a week and the component that tutorials, framework docs, and data-fetching libraries assume when they talk about error boundaries. TanStack Query's reset flow and most fallback UI examples are written against this API.

Use it if

  • You want a retry story rather than a dead screen: the fallback gets resetErrorBoundary, and resetKeys clears the boundary on its own when the route id or query parameter that caused the failure changes
  • You need to surface errors that React will never catch on its own, such as a failed fetch inside an onClick handler, by calling showBoundary from the useErrorBoundary hook
  • You want one place to report render errors, since onError receives the thrown value plus React's component stack and can hand both to your logging service
  • You are wrapping several independent regions of a page so one broken widget does not take down the whole route, and you want each region's fallback written inline
  • You are on a non-DOM renderer such as React Native, where framework-level error pages do not exist
Skip it if

Setup reality

The install is one command with no runtime dependencies and a peer dependency on React 18 or 19. Three things then catch people out. Version 6 is published as ES modules, and the README says projects on runtimes that cannot load ESM should stay on version 5; a Jest config that does not transform this package will throw on import. In any React Server Components framework the file holding the boundary needs a "use client" directive, and the props you pass across the boundary have to be serializable. And in development the error still appears: React logs the caught error to the console and the Vite or Next.js overlay still covers the screen, which routinely convinces people the boundary is not working when it is; close the overlay and the fallback is there. On the TypeScript side, the one error you will hit is "ErrorBoundary cannot be used as a JSX component", which is a duplicate @types/react in the tree rather than a bug here, and the documented fix is an overrides or resolutions pin. You must also pass exactly one of fallback, fallbackRender, or FallbackComponent; the types mark the other two as never, so passing two is a compile error rather than a silent precedence rule.

Patterns

Wrap a subtree with a retryable fallbackbasic-boundary

"use client";

import { ErrorBoundary, getErrorMessage } from "react-error-boundary";

export function Page() {
  return (
    <ErrorBoundary
      fallbackRender={({ error, resetErrorBoundary }) => (
        <div role="alert">
          <p>Something went wrong: {getErrorMessage(error)}</p>
          <button onClick={resetErrorBoundary}>Try again</button>
        </div>
      )}
    >
      <Dashboard />
    </ErrorBoundary>
  );
}

Pass exactly one of fallback, fallbackRender, or FallbackComponent; the types declare the other two as never. In development React still logs the error and your dev overlay still appears over the fallback, which is expected and does not happen in a production build.

Extract the fallback into a typed componentfallback-component

import { ErrorBoundary, type FallbackProps } from "react-error-boundary";

function Fallback({ error, resetErrorBoundary }: FallbackProps) {
  return (
    <div role="alert">
      <pre>{String(error)}</pre>
      <button onClick={resetErrorBoundary}>Reload section</button>
    </div>
  );
}

<ErrorBoundary FallbackComponent={Fallback}>
  <Widget />
</ErrorBoundary>;

FallbackProps types error as unknown rather than Error, because JavaScript lets you throw anything. Narrow it before reading .message, or call getErrorMessage, which returns undefined for values that have no message.

Clear the error automatically when state changesreset-keys

<ErrorBoundary
  resetKeys={[userId, filter]}
  onReset={(details) => {
    if (details.reason === "keys") {
      // reset caused by resetKeys changing
    } else {
      // reason === "imperative-api": user clicked retry
    }
  }}
  FallbackComponent={Fallback}
>
  <UserReport userId={userId} filter={filter} />
</ErrorBoundary>

resetKeys is compared by reference on each render, so an inline array or object literal in that list resets the boundary on every render and hides the error immediately. Keep the entries to primitives or memoized values.

Send an event handler or async error to the boundaryasync-error

import { useErrorBoundary } from "react-error-boundary";

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

  async function onClick() {
    try {
      await save();
    } catch (error) {
      showBoundary(error);
    }
  }

  return <button onClick={onClick}>Save</button>;
}

This is the workaround for React's biggest boundary limitation: nothing thrown from an event handler or a promise settled after render reaches a boundary on its own. The hook only works inside an ErrorBoundary subtree, so a component rendered above the boundary gets nothing.

Log every caught error with its component stackreport-errors

<ErrorBoundary
  fallback={<p>This section is unavailable.</p>}
  onError={(error, info) => {
    logger.error("render_error", {
      message: getErrorMessage(error),
      componentStack: info.componentStack,
    });
  }}
>
  <Chart />
</ErrorBoundary>

onError fires from componentDidCatch, so it runs after React has already committed the fallback. info.componentStack is the only place you get the React tree path, and it is far more useful than the JavaScript stack when the error came from a shared component.

Handle only some errors and pass the rest upwardrethrow-unhandled

function Fallback({ error, resetErrorBoundary }: FallbackProps) {
  if (!(error instanceof ChartDataError)) {
    throw error;   // bubbles to the parent boundary
  }

  return <button onClick={resetErrorBoundary}>Reload chart</button>;
}

A boundary cannot catch an error thrown from its own fallback, so rethrowing there hands the error to the next boundary up. That is how you build a specific widget boundary underneath a generic app-level one without the widget swallowing unrelated failures.

Wrap an existing component without touching its JSXhoc-wrapper

import { withErrorBoundary } from "react-error-boundary";

const SafeChart = withErrorBoundary(Chart, {
  fallback: <p>Chart failed to load.</p>,
  onError: reportError,
});

Useful for components rendered from a registry or a config-driven layout where you never write their JSX by hand. It forwards refs, but the boundary configuration is fixed at wrap time, so anything that has to depend on props needs the component form instead.

Isolate regions so one failure does not blank the pagegranular-boundaries

<Layout>
  <ErrorBoundary fallback={<SidebarError />}>
    <Sidebar />
  </ErrorBoundary>

  <ErrorBoundary fallback={<FeedError />} resetKeys={[feedId]}>
    <Feed feedId={feedId} />
  </ErrorBoundary>
</Layout>

Boundaries only catch below themselves, so the placement is the design. One boundary at the root turns any component error into a blank app; one per independently loaded region keeps the rest of the page alive and gives the user something to retry.

Combine with Suspense for loading and failuresuspense-and-boundary

<ErrorBoundary FallbackComponent={Fallback} resetKeys={[queryKey]}>
  <Suspense fallback={<Skeleton />}>
    <AsyncList queryKey={queryKey} />
  </Suspense>
</ErrorBoundary>

Order matters: the boundary must sit outside Suspense, otherwise a thrown promise reaches the boundary before Suspense sees it. When the retry needs to refetch, wire resetKeys or onReset to whatever cache your data layer uses, since resetting the boundary alone just re-renders the same failed state.

Let a React 19 transition route the error for youtransition-actions

function AddComment() {
  const [isPending, startTransition] = useTransition();

  return (
    <button
      disabled={isPending}
      onClick={() =>
        startTransition(async () => {
          await addComment();   // a rejection reaches the nearest boundary
        })
      }
    >
      {isPending ? "Adding..." : "Add comment"}
    </button>
  );
}

On React 19 an error thrown inside the function passed to startTransition is caught by the nearest boundary, so this replaces the manual showBoundary call for actions. It does nothing for plain event handlers that are not wrapped in a transition.

Get a message out of a value that may not be an Errorread-thrown-value

import { getErrorMessage } from "react-error-boundary";

const message = getErrorMessage(error) ?? "Unexpected error";

Third-party code and some transpiled async paths throw strings, plain objects, or promise rejection values. getErrorMessage returns string | undefined instead of assuming error.message exists, which stops the fallback itself from throwing and escalating to the parent boundary.

Fix ErrorBoundary cannot be used as a JSX componenttypes-react-mismatch

// package.json (npm)
{
  "overrides": { "@types/react": "19.0.0" }
}

// package.json (yarn)
{
  "resolutions": { "@types/react": "19.0.0" }
}

This TypeScript error means two different @types/react versions resolved in your tree, usually dragged in by another component library, and pinning one version fixes it. Run npm ls @types/react first to confirm that is what you are looking at.

Alternatives

PackageRegistryPick it when
@sentry/reactnpmYou already send errors to Sentry and would rather have one component that both renders a fallback and reports the error with the release and user context attached
@bugsnag/plugin-reactnpmBugsnag is your reporting backend and you want its boundary wired to the same client instance
@rollbar/reactnpmRollbar is your reporting backend and you want its provider plus boundary rather than a standalone one