# Error handler


React 19 introduced `onCaughtError`, `onUncaughtError`, and `onRecoverableError` callbacks on `createRoot` and `hydrateRoot`. These are root-level error handlers that catch errors without requiring an error boundary wrapper.

`flareReactErrorHandler` is a wrapper function that returns a callback compatible with these handlers. It automatically reports errors to Flare with React-specific context, including structured component stack traces. It also handles non-Error values (strings, objects) by converting them to proper Error instances.

## Basic usage

Pass `flareReactErrorHandler()` to the error callbacks when creating your root:

```jsx
import { flareReactErrorHandler } from '@flareapp/react';
import { createRoot } from 'react-dom/client';

const root = createRoot(document.getElementById('root'), {
    onCaughtError: flareReactErrorHandler(),
    onUncaughtError: flareReactErrorHandler(),
    onRecoverableError: flareReactErrorHandler(),
});

root.render(<App />);
```

## Callback lifecycle

The handler supports the same callback pattern as the [error boundary](/docs/react/errors/error-boundary), with three hooks that fire at different stages:

1. **`beforeEvaluate`** - called after the error is converted to an `Error`, before building the component stack context. Use this to attach custom context to Flare (e.g. user info, feature flags).
2. **`beforeSubmit`** - called with the built context. Return a (possibly modified) context object to filter or enrich the report before it is sent. See the [error boundary docs](/docs/react/errors/error-boundary#lifecycle-hooks) for the context type shape.
3. **`afterSubmit`** - called after the error is reported to Flare. Note that the report is sent asynchronously, so this callback runs after the report is initiated, not after the network request completes. Use this for side effects like logging or showing a toast.

All three callbacks receive `{ error, errorInfo }`, and `beforeSubmit` / `afterSubmit` additionally receive `context`. Unlike the error boundary, `errorInfo` here is not an instance of React's `ErrorInfo` class. It is the plain object React passes to `onCaughtError`, `onUncaughtError`, and `onRecoverableError`, shaped as `{ componentStack?: string }`.

The callbacks are not wrapped in a `try`/`catch`. If a hook throws, the error will bubble out of the React error callback, so keep the logic defensive.

These hooks are separate from `flare.configure({ beforeEvaluate, beforeSubmit })`. The handler hooks add context and run side effects but cannot suppress errors. To filter errors, use the client-level hooks. See [Filtering errors](/docs/react/errors/error-boundary#filtering-errors) for the full execution order.

`flareReactErrorHandler` also accepts a `flare` option, an explicit Flare instance to report through instead of the default singleton. You only need this outside a normal web setup, for example when using `@flareapp/react/inject`. See the [API reference](/docs/react/reference/api).

```jsx
import { flare } from '@flareapp/js';
import { flareReactErrorHandler } from '@flareapp/react';

const root = createRoot(document.getElementById('root'), {
    // Errors caught by an Error Boundary
    onCaughtError: flareReactErrorHandler({
        afterSubmit: ({ error, errorInfo }) => {
            console.warn('Caught error:', error);
        },
    }),

    // Errors NOT caught by any Error Boundary
    onUncaughtError: flareReactErrorHandler({
        beforeEvaluate: ({ error }) => {
            flare.addContext('user', { id: currentUser.id });
        },
        afterSubmit: ({ error }) => {
            console.error('Uncaught error:', error);
        },
    }),

    // Errors React recovers from automatically (e.g. hydration mismatches)
    onRecoverableError: flareReactErrorHandler(),
});

root.render(<App />);
```

## Usage with `hydrateRoot`

If your app uses server-side rendering (Next.js, Remix, etc.), you can use `flareReactErrorHandler` with `hydrateRoot` the same way:

```jsx
import { flareReactErrorHandler } from '@flareapp/react';
import { hydrateRoot } from 'react-dom/client';

hydrateRoot(document.getElementById('root'), <App />, {
    onUncaughtError: flareReactErrorHandler(),
    onRecoverableError: flareReactErrorHandler(),
});
```

## Compatibility with `react-error-boundary`

`flareReactErrorHandler()` returns a callback that matches the `onError` prop signature of the popular [`react-error-boundary`](https://github.com/bvaughn/react-error-boundary) library. If you're already using that library, you can report errors to Flare without switching to `FlareErrorBoundary`:

```jsx
import { ErrorBoundary } from 'react-error-boundary';
import { flareReactErrorHandler } from '@flareapp/react';

<ErrorBoundary
    onError={flareReactErrorHandler()}
    fallback={<p>Something went wrong.</p>}
>
    <App />
</ErrorBoundary>
```

## When to use the error handler vs. the error boundary

| | Error boundary | Error handler |
|---|---|---|
| React version | 16+ | 19+ |
| Catches render errors | Yes | Yes |
| Displays fallback UI | Yes | No |
| Reset capability | Yes | No |
| Works with `createRoot` callbacks | No | Yes |

You can use both together: the error boundary for displaying fallback UI and the error handler for catching errors at the root level. Note that `onCaughtError` is omitted here because the `FlareErrorBoundary` already reports caught errors, and adding `onCaughtError` would cause them to be reported twice.

```jsx
import { FlareErrorBoundary, flareReactErrorHandler } from '@flareapp/react';
import { createRoot } from 'react-dom/client';

const root = createRoot(document.getElementById('root'), {
    // onCaughtError is omitted because FlareErrorBoundary already handles these.
    onUncaughtError: flareReactErrorHandler(),
    onRecoverableError: flareReactErrorHandler(),
});

root.render(
    <FlareErrorBoundary fallback={<p>Something went wrong.</p>}>
        <App />
    </FlareErrorBoundary>
);
```
