Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sentry - React - capture errors iframe and only iframe

Tags:

reactjs

sentry

Context
We're developing a React-based app that is being used as a 'widget' on other websites. Our entire react app is wrapped in an iframe using react-frame-component.

For developing purposes, we use Sentry to log any errors (@sentry/browser sdk).

Problem
We noticed that once our app is integrated on any website errors don't get logged to Sentry anymore and we're not entirely sure how to solve this.

So we're seeking a solution to log errors to Sentry that occur inside an iframe and ONLY inside that iframe.

Is it possible to somehow tell Sentry what window/target/scope to use?

Using React Error Boundaries might be an option to consider. However, it doesn't capture all errors.

Iframe - code

...
render(
    <Iframe initialContent={initialIframeContent}>
        <App />
    </Iframe>,
    document.getElementById(containerId)
);
...

Sentry integration - code

...
Sentry.init({
    environment: env,
    dsn: SENTRY_DSN
});
...
like image 498
Jim Vercoelen Avatar asked Sep 17 '25 01:09

Jim Vercoelen


1 Answers

We couldn't find a direct solution so we ended up using React Error Boundaries to capture any crash and log it to sentry.

Keep in mind that Error Boundaries doesn't capture all crashes (see quote below). So we came up with a hook that would make Error Boundaries capture those crashes as well (except for the server side rendering and crashes inside Error Boundaries itself).

See inspiration hook.

Note

Error boundaries do not catch errors for:

  • Event handlers (learn more)
  • Asynchronous code (e.g. setTimeout or requestAnimationFrame callbacks)
  • Server side rendering
  • Errors thrown in the error boundary itself (rather than its children)

~ React

ErrorBoundary.js

const initialState = {
    hasError: false,
    showError: false
};

export default class ErrorBoundary extends Component {
    static propTypes = {
        children: PropTypes.node.isRequired
    };

    static getDerivedStateFromError () {
        return { hasError: true };
    }

    state = initialState;

    componentDidCatch (error) {
        logToSentry(error);
    };
    ...

See sentry docs

useError.js

const useError = () => {
    const [_, setError] = useState();

    return useCallback(
        e => {
            setError(() => {
                throw e;
            });
        },
        [setError],
    );
};

export default useError;

Usage sample

const throwError = useError();

const someAsyncFn = useCallback(async () => {
    try {
        const response = await asyncApiRequest();
    } catch (e) {
        throwError(e)
    }
}, []);

const someEventHandler = useCallback(async () => {
    try {
        doSomethingThatMightCrash();
    } catch (e) {
        throwError(e)
    }
}, []);

It's a bit tedious and there might be ways to simplify this even more but it does its job.

like image 59
Jim Vercoelen Avatar answered Sep 19 '25 15:09

Jim Vercoelen