ErrTapDocs

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-native

The 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.map

ERRTAP_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 --sourcemap at it.

Options

FieldTypeDescription
dsnstringURL DSN (https://et_…@host) or bare key (requires endpoint)
endpointstringOverride ingest URL; optional when dsn is a URL DSN
environmentstringe.g. production, development
releasestringApp version — ties errors to a release; match it on the sourcemap upload
tagsRecord<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.