useErrorBoundary allows you to catch errors that occur in child components and handle them gracefully. It provides both the caught error and a function to reset the error state.
Signature
function useErrorBoundary(
callback?: (error: any, errorInfo: ErrorInfo) => Promise<void> | void
): [any, () => void]
Parameters
callback
(error: any, errorInfo: ErrorInfo) => Promise<void> | void
An optional callback function that is invoked when an error is caught. Receives the error and additional error information (component stack trace). Can be used for error logging or reporting.
Returns
Returns a tuple containing:
- Error (
any): The caught error, or undefined if no error has occurred
- Reset function (
() => void): A function to clear the error state and attempt to re-render
Basic Usage
import { useErrorBoundary } from 'preact/hooks';
function BuggyComponent() {
throw new Error('Something went wrong!');
return <div>This won't render</div>;
}
function App() {
const [error, resetError] = useErrorBoundary();
if (error) {
return (
<div>
<h1>Error Occurred</h1>
<p>{error.message}</p>
<button onClick={resetError}>Try Again</button>
</div>
);
}
return <BuggyComponent />;
}
With Error Logging
function App() {
const [error, resetError] = useErrorBoundary((error, errorInfo) => {
// Log error to an error reporting service
console.error('Error caught:', error);
console.error('Component stack:', errorInfo.componentStack);
// Send to error tracking service
logErrorToService({
message: error.message,
stack: error.stack,
componentStack: errorInfo.componentStack
});
});
if (error) {
return (
<div className="error-screen">
<h1>Oops! Something went wrong</h1>
<button onClick={resetError}>Reload</button>
</div>
);
}
return <MainApp />;
}
Async Error Logging
function App() {
const [error, resetError] = useErrorBoundary(async (error, errorInfo) => {
try {
await fetch('/api/log-error', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
error: error.message,
stack: error.stack,
componentStack: errorInfo.componentStack,
timestamp: new Date().toISOString()
})
});
} catch (logError) {
console.error('Failed to log error:', logError);
}
});
if (error) {
return (
<div>
<h1>Error: {error.message}</h1>
<button onClick={resetError}>Retry</button>
</div>
);
}
return <App />;
}
Granular Error Boundaries
function UserProfile({ userId }) {
const [error, resetError] = useErrorBoundary();
if (error) {
return (
<div className="profile-error">
<p>Failed to load profile</p>
<button onClick={resetError}>Retry</button>
</div>
);
}
return <ProfileContent userId={userId} />;
}
function App() {
return (
<div>
<Header />
<UserProfile userId={123} />
<UserProfile userId={456} />
<Footer />
</div>
);
}
With Fallback UI
function ErrorFallback({ error, resetError }) {
return (
<div className="error-container">
<h2>Something went wrong</h2>
<details>
<summary>Error details</summary>
<pre>{error.message}</pre>
<pre>{error.stack}</pre>
</details>
<button onClick={resetError}>Try again</button>
</div>
);
}
function App() {
const [error, resetError] = useErrorBoundary((error) => {
console.error('Caught error:', error);
});
if (error) {
return <ErrorFallback error={error} resetError={resetError} />;
}
return <MainContent />;
}
Multiple Error Boundaries
function Section({ title, children }) {
const [error, resetError] = useErrorBoundary();
if (error) {
return (
<div className="section-error">
<h3>{title} - Error</h3>
<p>{error.message}</p>
<button onClick={resetError}>Retry</button>
</div>
);
}
return (
<section>
<h3>{title}</h3>
{children}
</section>
);
}
function App() {
return (
<div>
<Section title="User Info">
<UserInfo />
</Section>
<Section title="Recent Activity">
<ActivityFeed />
</Section>
<Section title="Settings">
<UserSettings />
</Section>
</div>
);
}
With State Reset
function DataList() {
const [data, setData] = useState([]);
const [error, resetError] = useErrorBoundary();
const handleReset = () => {
setData([]); // Reset component state
resetError(); // Clear error state
};
if (error) {
return (
<div>
<p>Failed to load data: {error.message}</p>
<button onClick={handleReset}>Reset and Retry</button>
</div>
);
}
return (
<ul>
{data.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
Error Reporting Service
const errorReportingService = {
report: async (error, errorInfo, context) => {
await fetch('https://api.example.com/errors', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
message: error.message,
stack: error.stack,
componentStack: errorInfo.componentStack,
userAgent: navigator.userAgent,
url: window.location.href,
context
})
});
}
};
function App() {
const [error, resetError] = useErrorBoundary((error, errorInfo) => {
errorReportingService.report(error, errorInfo, {
user: getCurrentUser(),
timestamp: Date.now(),
version: APP_VERSION
});
});
if (error) {
return (
<div>
<h1>Application Error</h1>
<p>We've been notified and are working on a fix.</p>
<button onClick={resetError}>Reload Application</button>
</div>
);
}
return <MainApp />;
}
Conditional Error Display
function App() {
const [error, resetError] = useErrorBoundary();
const [showDetails, setShowDetails] = useState(false);
if (error) {
return (
<div className="error-page">
<h1>An error occurred</h1>
<p>{error.message}</p>
{showDetails && (
<pre className="error-stack">{error.stack}</pre>
)}
<div className="error-actions">
<button onClick={resetError}>Try Again</button>
<button onClick={() => setShowDetails(!showDetails)}>
{showDetails ? 'Hide' : 'Show'} Details
</button>
</div>
</div>
);
}
return <App />;
}
useErrorBoundary only catches errors in child components during rendering, in lifecycle methods, and in constructors. It does not catch errors in:
- Event handlers (use try-catch for these)
- Asynchronous code (setTimeout, promises)
- Server-side rendering
- Errors thrown in the error boundary itself
The error boundary will catch errors from all descendant components. Place error boundaries strategically to provide appropriate fallback UIs at different levels of your component tree.
Always call resetError() before attempting to re-render components that previously threw errors. This clears the error state and allows the error boundary to catch new errors.