React Native SDK
Install @errtap/react-native to capture uncaught JavaScript errors in React Native and Expo apps via the global ErrorUtils handler.
Install
npm i @errtap/react-nativeThe SDK reads React Native's supported global ErrorUtils runtime handler for
uncaught JS errors, forwards them to ErrTap, then hands off to the previous
handler. Repeated init calls update configuration without stacking handlers.
Production Hermes promise rejections are captured only when Hermes exposes its
native rejection-tracker hook; other runtimes are not monkey-patched.
Initialise
Call init once, as early as possible in your entry file:
// App.tsx (or index entry — call once at startup)
import { init } from '@errtap/react-native';
init({
dsn: process.env.EXPO_PUBLIC_ERRTAP_DSN!, // https://et_…@host
environment: __DEV__ ? 'development' : 'production',
release: '1.0.0',
});With Expo, expose the DSN through an EXPO_PUBLIC_-prefixed env var. DSN keys are
write-only, so shipping one in the app binary is by design.
Keep crash reports that happen offline
A fatal JS error ends a release build, so ErrTap holds React Native's crash handler for up to 2 seconds while the report is sent. Pass a key-value store and a crash that can't be sent in time (offline, flaky network) is saved and delivered on next launch:
import AsyncStorage from '@react-native-async-storage/async-storage';
init({ dsn: process.env.EXPO_PUBLIC_ERRTAP_DSN!, release: '1.0.0', storage: AsyncStorage });Manual capture & logs
import { captureException, captureMessage, logger } from '@errtap/react-native';
logger.info('checkout started'); // also kept as a breadcrumb
try {
await pay();
} catch (e) {
captureException(e as Error, { tags: { screen: 'Checkout' } });
throw e;
}Each log line is also kept as a breadcrumb, and the last 20 are attached to the next error's context. Capture calls are fire-and-forget and never throw.
Source maps (de-minify stack traces)
Release bundles are minified, so production traces read like index.android.bundle:1:428079.
ErrTap de-minifies them at ingest when it has the bundle's source map for that release.
@errtap/react-native ships an errtap-upload-sourcemaps command; run it once per platform
after you build a release bundle with a sourcemap:
# produce a release bundle + sourcemap (Android shown; iOS: main.jsbundle)
npx react-native bundle --platform android --dev false \
--entry-file index.js \
--bundle-output index.android.bundle \
--sourcemap-output index.android.bundle.map
# upload the map, keyed by the release you pass to init()
ERRTAP_AUTH_TOKEN=etu_<token> ERRTAP_DSN=https://et_<key>@your-host ERRTAP_RELEASE=1.0.0 \
npx errtap-upload-sourcemaps --sourcemap index.android.bundle.mapERRTAP_AUTH_TOKEN is an upload token from Project settings → Upload tokens. It's a
secret, so keep it in CI — never bundle it into the app. The DSN only tells the uploader
which host to use.
--bundle defaults to the sourcemap name minus .map (the filename the runtime stack
references); pass it explicitly if they differ. The map isn't deleted — it's a build
artifact, not part of your app.
Hermes: upload the composed map (the Metro map composed with the Hermes bytecode map via
react-native/scripts/compose-source-maps.js), or the columns won't match the runtime stack. Your release build usually emits this as the final.map— point--sourcemapat it.
Options
| Field | Type | Description |
|---|---|---|
dsn | string | URL DSN (https://et_…@host) or bare key (requires endpoint) |
endpoint | string | Override ingest URL; optional when dsn is a URL DSN |
environment | string | e.g. production, development |
release | string | App version — ties errors to a release; match it on the sourcemap upload |
tags | Record<string, unknown> | Attached to every event |
storage | { getItem, setItem, removeItem } | Key-value store (e.g. AsyncStorage) that keeps fatal crash reports until they're sent |
Every event carries platform: react-native as a tag and os and osVersion in its
context. Errors caught by the global ErrorUtils handler also get a fatal: true|false tag;
manual captures and unhandled rejections don't. The SDK has no user feedback or Web Vitals support; send feedback from
your backend with @errtap/node or /ingest/feedback.