Skip to content

React Native + Expo SDK

The official Pionne SDK for React Native and Expo. Auto-captures JS errors, native iOS/Android errors, and Hermes errors. Works with Expo managed and bare workflow.

Current version: 0.8.6

Terminal window
npm install @pionne/react-native
import { Pionne } from '@pionne/react-native';
Pionne.init({ token: 'pio_live_…' });

To stop polluting your prod dashboard with debug events from Metro / Expo Go (and dodge the bundle ID mismatch headache between Expo Go’s host.exp.Exponent and the bundle pinned on your project), pass enableInDev: false:

Pionne.init({
token: 'pio_live_…',
enableInDev: false, // no-op in __DEV__
});

In __DEV__, init() logs [Pionne] Skipped in __DEV__ (enableInDev=false) and every method (captureException, captureMessage, setUser, etc.) is a silent no-op for the lifetime of the process. No global handlers installed, no session opened. Default behavior: enableInDev: true (backward compat).

JS handlers (ErrorUtils, the promise tracker, the Error Boundary) can only see crashes on the JS thread. A native crash tears down the whole process, JS VM included, before any JS can run. So Pionne leans on the OS to record them and replays them as fatal events on the next launch.

On by default. To disable:

Pionne.init({
token: 'pio_live_…',
captureNativeCrashes: false,
});

Via MetricKit (MXCrashDiagnostic):

  • Objective-C / Swift NSException — exception name + composed message on iOS 17+ (e.g. NSInvalidArgumentException)
  • SignalsSIGSEGV, SIGABRT, SIGBUS, SIGILL, SIGFPE, SIGTRAP
  • Out-of-memory kills and watchdog terminations (0x8badf00d)
  • Call stack tree — system frames symbolicated by the OS; app frames as binaryName 0xADDRESS (no dSYM symbolication step)

Via ActivityManager.getHistoricalProcessExitReasons():

  • REASON_CRASH — unhandled JVM exception
  • REASON_CRASH_NATIVE — NDK / native (C/C++) crash
  • REASON_ANR — Application Not Responding (with the ANR trace)
  • REASON_LOW_MEMORY — OOM kill

Native crashes arrive on the launch after the crash (the OS delivers them post-mortem), with mechanism.type = "native" and a native.source tag (metrickit on iOS, app_exit on Android).

On the very narrow surface of iOS 26 + new architecture in production, React Native re-throws JS errors through a void TurboModule on an async dispatch queue, which the runtime aborts as an unrecoverable SIGABRT (RN #54859). Since 0.9.1, the SDK detects that exact combination and suppresses the re-throw after capturing the event — the host app keeps running. Every other runtime (older iOS, Android, old architecture, dev mode) keeps the default React Native behaviour.

This page is an alias for the React Native SDK’s main documentation. The full guides:

Pionne can attach the user’s approximate city/region/country to each event. Off by default for privacy:

Pionne.init({
token: 'pio_live_…',
sendGeography: true, // ← opt-in
});

At boot, a single HTTP call to https://ipapi.co/json/ (4 s timeout) resolves contexts.geo = { city, region, country, country_code } and attaches it to every subsequent event. No iOS/Android permission required (no GPS, just IP reverse-lookup). If the lookup fails, the SDK keeps working without geo.

You can swap the provider via geographyEndpoint: '…' — any URL returning JSON { city, region, country, country_code } works.

The bundle ID (com.yourapp.app) is auto-pinned server-side on the first event received. If someone steals your token and tries to use it from another app, those events get rejected automatically.