@andrewkepson/react-error-boundary
React error boundary with fallback UI, reset, error callbacks, and context tagging.
Install
npm install @andrewkepson/react-error-boundary
Requires React 16.8+.
Note: This package uses class components, which are client-side only. For Next.js App Router or other SSR frameworks, wrap usage in "use client" directives or use it in pages/layouts that render on the client.
Usage
Default fallback
import { ErrorBoundary } from '@andrewkepson/react-error-boundary';
<ErrorBoundary errorContext="CheckoutProviders">
<CheckoutFlow />
</ErrorBoundary>
Renders a minimal role="alert" UI with the context.
Note: The default fallback does not render error.message to prevent accidentally leaking sensitive information (like API keys or internal file paths) into the DOM in production. If you need to display the error message to users, use a custom fallback render function.
Custom fallback node
<ErrorBoundary fallback={<FullPageError />}>
<App />
</ErrorBoundary>
Fallback render function with reset
The fallback render function runs during the boundary's own render, so defining the fallback UI inline here is fine — there's no separate component re-rendering to optimize. The reset callback it hands you is stable:
function renderFallback(error: Error, reset: () => void) {
return (
<div role="alert">
<p>{error.message}</p>
<button onClick={reset}>Retry</button>
</div>
);
}
function App() {
return (
<ErrorBoundary fallback={renderFallback}>
<Dashboard />
</ErrorBoundary>
);
}
This is the intended pattern for surfacing error.message — opt in explicitly where you control the context, rather than leaking it from the default fallback.
Hoisting renderFallback to module scope (as above) gives it a stable identity for free. Only reach for useCallback when the fallback must close over props/state from the parent and that parent re-renders often enough to matter — otherwise a plain function is the right call.
Async errors with useErrorBoundary
For async/data-loading errors, use useErrorBoundary to pass caught errors to the nearest boundary:
import { ErrorBoundary, useErrorBoundary } from '@andrewkepson/react-error-boundary';
function UserProfile({ username }: { username: string }) {
const { showBoundary, resetBoundary } = useErrorBoundary();
async function loadProfile() {
try {
await fetchUserProfile(username);
resetBoundary();
} catch (err) {
if (err instanceof Error) {
showBoundary(err);
}
}
}
return (
<button onClick={loadProfile}>
Load profile
</button>
);
}
function App() {
return (
<ErrorBoundary fallback={<p>Could not load profile</p>}>
<UserProfile username="andrew" />
</ErrorBoundary>
);
}
This hook returns { resetBoundary, showBoundary } for handling errors in async flows within React components. Use it to manually trigger or clear errors within components protected by an ErrorBoundary.
Safe error message extraction with getErrorMessage
Use getErrorMessage to safely extract error messages for display (handles non-Error objects):
import { ErrorBoundary, getErrorMessage } from '@andrewkepson/react-error-boundary';
function renderFallback(error: Error) {
return (
<div role="alert">
<p>{getErrorMessage(error)}</p>
{/* ... */}
</div>
);
}
function App() {
return (
<ErrorBoundary fallback={renderFallback}>
<Dashboard />
</ErrorBoundary>
);
}
This is the intended pattern for surfacing error.message — opt in explicitly where you control the context, rather than leaking it from the default fallback.
Error reporting
onError receives the error, React error info, and the boundary's errorContext — useful for attributing errors when one handler serves many boundaries. Define it as a named function rather than inline so a frequently re-rendering parent doesn't hand the boundary a new prop identity every render:
function reportError(error: Error, errorInfo: ErrorInfo, errorContext?: string) {
sentry.captureException(error, {
tags: { boundary: errorContext },
extra: { componentStack: errorInfo.componentStack },
});
}
function Layout() {
return (
<ErrorBoundary errorContext="Sidebar" onError={reportError}>
<Sidebar />
</ErrorBoundary>
);
}
If the handler needs values from the component's scope, wrap it in useCallback so its identity stays stable across renders — but only when it actually closes over changing values:
function Layout({ userId }: { userId: string }) {
const handleError = useCallback(
(error: Error, errorInfo: ErrorInfo) => {
sentry.captureException(error, {
tags: { userId },
extra: { componentStack: errorInfo.componentStack },
});
},
[userId],
);
return (
<ErrorBoundary errorContext="Sidebar" onError={handleError}>
<Sidebar />
</ErrorBoundary>
);
}
Auto-reset with resetKeys
The boundary resets when any key changes (compared with Object.is). Pair with route params or resource IDs so navigation clears the error state:
<ErrorBoundary resetKeys={[userId]} fallback={<ErrorState />}>
<UserProfile userId={userId} />
</ErrorBoundary>
The inline [userId] array is intentional — the boundary compares keys by value, not by array identity, so a fresh array each render is harmless. Don't wrap it in useMemo.
Props
| Prop | Type | Description |
|---|---|---|
children |
ReactNode |
Tree to protect. |
fallback |
ReactNode | ((error, reset) => ReactNode) |
UI to show on error. Function form receives the error and a reset callback. Defaults to a minimal built-in UI (which omits error.message for security). |
onError |
(error, errorInfo, errorContext?) => void |
Called in componentDidCatch. Use for logging/reporting. |
onReset |
() => void |
Called when the boundary resets (manual or via resetKeys). |
resetKeys |
readonly unknown[] |
Boundary resets when any key changes. |
errorContext |
string |
Label for this boundary, rendered in the default fallback and passed to onError. |
License
MIT