nteract
liberror-boundarysource: ok · hit
Error Boundary
React error boundary with resetKeys support for automatic recovery. Includes OutputErrorFallback and WidgetErrorFallback components for graceful error display in outputs and widgets.
npx shadcn add "https://nteract-elements.vercel.app/r/error-boundary.json"free
No selection tags yet — empty means no evidence, not lack of support.
Files3 · source cached 10/14/2026, 3:38:59 PM
registry/lib/error-boundary.tsx
"use client";
import { Component, type ErrorInfo, type ReactNode } from "react";
export interface ErrorBoundaryProps {
children: ReactNode;
/** Render prop called when an error is caught. Return the fallback UI. */
fallback: (error: Error, resetErrorBoundary: () => void) => ReactNode;
/** Optional callback when an error is caught. */
onError?: (error: Error, errorInfo: ErrorInfo) => void;
/**
* When any value in this array changes, the error state is automatically
* cleared and the children are re-rendered. Use this to recover from
* errors when the underlying data changes (e.g., cell re-execution,
* new output data, widget state update).
*/
resetKeys?: readonly unknown[];
}
interface ErrorBoundaryState {
error: Error | null;
prevResetKeys: readonly unknown[] | undefined;
}
/**
* Reusable React error boundary.
*
* Catches render errors in child components and displays a fallback UI
* via the `fallback` render prop. Provides a `resetErrorBoundary` function
* to retry rendering.
*
* Supports automatic recovery via `resetKeys` — when any key changes,
* the error state is cleared and children re-render.
*
* @example
* ```tsx
* <ErrorBoundary
* fallback={(error, reset) => (
* <div>
* <p>Something went wrong: {error.message}</p>
* <button onClick={reset}>Retry</button>
* </div>
* )}
* resetKeys={[executionCount]}
* >
* <MyComponent />
* </ErrorBoundary>
* ```
*/
export class ErrorBoundary extends Component<
ErrorBoundaryProps,
ErrorBoundaryState
> {
constructor(props: ErrorBoundaryProps) {
super(props);
this.state = { error: null, prevResetKeys: props.resetKeys };
}
static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState> {
return { error };
}
static getDerivedStateFromProps(
props: ErrorBoundaryProps,
state: ErrorBoundaryState,
): Partial<ErrorBoundaryState> | null {
if (
state.error &&
state.prevResetKeys !== undefined &&
props.resetKeys !== undefined
) {
const changed =
props.resetKeys.length !== state.prevResetKeys.length ||
props.resetKeys.some(
(key, i) => !Object.is(key, state.prevResetKeys![i]),
);
if (changed) {
return { error: null, prevResetKeys: props.resetKeys };
}
}
// Always track latest resetKeys even when not in error state
if (props.resetKeys !== state.prevResetKeys) {
return { prevResetKeys: props.resetKeys };
}
return null;
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error("[ErrorBoundary]", error, errorInfo.componentStack);
this.props.onError?.(error, errorInfo);
}
resetErrorBoundary = () => {
this.setState({ error: null });
};
render() {
if (this.state.error) {
return this.props.fallback(this.state.error, this.resetErrorBoundary);
}
return this.props.children;
}
}
registry/lib/output-error-fallback.tsx
"use client";
import { cn } from "@/lib/utils";
export interface OutputErrorFallbackProps {
/** The error that was caught */
error: Error;
/** Optional index of the output (0-based) for display */
outputIndex?: number;
/** Callback to retry rendering */
onRetry?: () => void;
}
/**
* Fallback UI for output rendering errors.
*
* Displays an error message with optional retry button.
* Styled to match AnsiErrorOutput patterns.
*/
export function OutputErrorFallback({
error,
outputIndex,
onRetry,
}: OutputErrorFallbackProps) {
return (
<div
data-slot="output-error"
className={cn(
"border-l-2 border-red-200 dark:border-red-800 py-3 pl-3 pr-2",
"rounded-r bg-red-50/50 dark:bg-red-950/20",
)}
>
<div className="flex items-start justify-between gap-2">
<div>
<div className="text-sm font-medium text-red-700 dark:text-red-400">
Output rendering failed
{outputIndex !== undefined && ` (output ${outputIndex + 1})`}
</div>
<div className="mt-1 font-mono text-xs text-red-600/80 dark:text-red-400/70">
{error.message}
</div>
</div>
{onRetry && (
<button
type="button"
onClick={onRetry}
className="text-xs text-red-600 underline hover:text-red-800 dark:text-red-400 dark:hover:text-red-300"
>
Retry
</button>
)}
</div>
</div>
);
}
registry/lib/widget-error-fallback.tsx
import { cn } from "@/lib/utils";
export interface WidgetErrorFallbackProps {
/** The error that was caught */
error: Error;
/** The widget model ID */
modelId: string;
/** Optional widget model name for display */
modelName?: string;
/** Callback to retry rendering */
onRetry?: () => void;
}
/**
* Fallback UI for widget rendering errors.
*
* Displays an error message with optional retry button.
* Styled to match UnsupportedWidget patterns.
*/
export function WidgetErrorFallback({
error,
modelId,
modelName,
onRetry,
}: WidgetErrorFallbackProps) {
return (
<div
className={cn(
"rounded border border-dashed border-red-300 p-3 text-sm dark:border-red-800",
"bg-red-50/50 dark:bg-red-950/20",
)}
data-widget-id={modelId}
data-widget-error="true"
>
<div className="flex items-start justify-between gap-2">
<div>
<div className="font-medium text-red-700 dark:text-red-400">
Widget error{modelName && `: ${modelName}`}
</div>
<div className="mt-1 font-mono text-xs text-red-600/70 dark:text-red-400/60">
{error.message}
</div>
</div>
{onRetry && (
<button
type="button"
onClick={onRetry}
className="text-xs text-red-600 underline hover:text-red-800 dark:text-red-400 dark:hover:text-red-300"
>
Retry
</button>
)}
</div>
</div>
);
}