ErrorBoundary
Catch and handle React component errors with declarative error boundaries.
The ErrorBoundary component catches JavaScript errors anywhere in its child component tree, logs the error, and displays a fallback UI instead of crashing the entire application.
Installation
npm install @zayne-labs/ui-reactPreview
Loading...
Basic Usage
Provide a static component or element to display when an error occurs.
import { ErrorBoundary } from "@zayne-labs/ui-react/common/error-boundary";
function App() {
return (
<ErrorBoundary fallback={<div className="p-4 border border-red-200 bg-red-50 text-red-700">Something went wrong</div>}>
<ComponentThatMightCrash />
</ErrorBoundary>
);
}Use a render function to access the error details and provide a way to reset the boundary.
import { ErrorBoundary } from "@zayne-labs/ui-react/common/error-boundary";
function App() {
return (
<ErrorBoundary
fallback={({ error, resetErrorBoundary }) => (
<div className="flex flex-col gap-4 p-8 border rounded-lg">
<h2 className="text-xl font-bold">Unexpected Error</h2>
<code className="text-sm p-2 bg-gray-100 rounded">{error.message}</code>
<button
onClick={resetErrorBoundary}
className="rounded-md bg-zu-primary px-4 py-2 text-white"
>
Try Again
</button>
</div>
)}
>
<ComponentThatMightCrash />
</ErrorBoundary>
);
}Component Source
"use client";import { isFunction } from "@zayne-labs/toolkit-type-helpers";import { Component } from "react";import { ErrorBoundaryContext } from "./error-boundary-context";import type { ErrorBoundaryProps } from "./types";type ErrorBoundaryState = | { error: Error; hasError: true; } | { error: null; hasError: false; };const initialState: ErrorBoundaryState = { error: null, hasError: false,};const hasArrayChanged = (arrayOne: unknown[] = [], arrayTwo: unknown[] = []) => { return ( arrayOne.length !== arrayTwo.length || arrayOne.some((item, index) => !Object.is(item, arrayTwo[index])) );};/** * Copied from react-error-boundary package * @see https://github.com/bvaughn/react-error-boundary */export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> { constructor(props: ErrorBoundaryProps) { super(props); this.state = initialState; } static getDerivedStateFromError(error: Error) { return { error, hasError: true }; } override componentDidCatch(error: Error, info: React.ErrorInfo) { this.props.onError?.({ error, info }); } override componentDidUpdate(prevProps: ErrorBoundaryProps, prevState: ErrorBoundaryState) { const { hasError } = this.state; const { errorResetKeys } = this.props; // == There's an edge case where if the thing that triggered the error happens to *also* be in the resetKeys array, we'd end up resetting the error boundary immediately. // == This would likely trigger a second error to be thrown. // == So we make sure that we don't check the resetKeys on the first call of cDU after the error is set. if ( hasError && prevState.error !== null && hasArrayChanged(prevProps.errorResetKeys, errorResetKeys) ) { this.props.onErrorReset?.({ next: errorResetKeys, prev: prevProps.errorResetKeys, reason: "keys", }); this.setState(initialState); } } override render() { const { children, errorFallback } = this.props; const { error, hasError } = this.state; let childToRender = children; if (hasError) { switch (true) { case isFunction(errorFallback): { childToRender = errorFallback({ error, resetErrorBoundary: this.#resetErrorBoundary, }); break; } case Boolean(errorFallback): { childToRender = errorFallback; break; } default: { console.warn("No fallback provided to error boundary"); } } return ( <ErrorBoundaryContext value={{ error, hasError, resetErrorBoundary: this.#resetErrorBoundary, }} > {childToRender} </ErrorBoundaryContext> ); } return childToRender; } #resetErrorBoundary = (...parameters: unknown[]) => { const { error } = this.state; if (error === null) return; this.props.onErrorReset?.({ args: parameters, reason: "imperative-api", }); this.setState(initialState); };}Component API
Best Practices
- Isolate Failures: Place boundaries around specific widgets or sections (like a sidebar or a feed) to prevent a single component failure from breaking the whole page.
- Auto-Reset: Use
resetKeyswith URL params or resource IDs so that navigating away and back "fixes" the component if it crashed. - Global Boundary: Keep a generic boundary at the very top of your app as a last resort.
Error boundaries do not catch errors in event handlers or asynchronous code (like setTimeout) by
default. Use the showBoundary method from useErrorBoundary to catch these manually.
Last updated on