Skip to main content
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:
  1. Error (any): The caught error, or undefined if no error has occurred
  2. 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.

Build docs developers (and LLMs) love