Error Tracking in React Native With Sentry (and the Alternatives) in 2026

Set up Sentry for React Native (SDK 8.33): wizard, Metro and Expo config, source maps, replay. Compared with Crashlytics, Datadog, Bugsnag and EAS Observe.

Error Tracking in React Native With Sentry (and the Alternatives) in 2026

A React Native app fails in two different worlds. JavaScript throws, and if nothing catches it the red screen appears in development while production users see the app freeze or restart. Native code crashes, and the process is gone before any JavaScript could notice. Error tracking for React Native has to cover both, link them to the same release and the same user, and give you a readable stack trace from minified Hermes bytecode. This guide sets up Sentry for React Native the way we would in a production app today (SDK 8.33.0, released October 29, 2026), then compares the alternatives: Bugsnag, Firebase Crashlytics, Datadog, PostHog and Expo’s own EAS Observe.

Why Sentry for React Native is the default

Three reasons, in order of weight. First, the @sentry/react-native package wraps the native Sentry SDKs for iOS (sentry-cocoa) and Android (sentry-java) and the JavaScript SDK in one install, so a native crash and a JavaScript exception land in the same project with the same release tag. Second, source maps and debug symbols are handled at build time by a Metro plugin, an Xcode build phase and a Gradle step, so stack traces are symbolicated without a separate upload pipeline. Third, momentum: the package did about 5.1 million npm downloads in the last week of October 2026 and has shipped a minor every Wednesday for the past six weeks (8.28 through 8.33). The gap to the next option, measured by downloads, is roughly seven to one.

Weekly npm downloads, week ending November 1, 2026:

Package Latest Released Downloads / week
@sentry/react-native 8.33.0 2026-10-29 5,098,583
@datadog/mobile-react-native 3.11.0 2026-10-20 689,620
@react-native-firebase/crashlytics 27.1.0 2026-10-23 684,671
@bugsnag/react-native 9.1.0 2026-10-09 129,802
react-native-exception-handler 2.10.10 2021-03-23 53,967

Downloads include CI runs and are inflated for every package equally, so read the ratios, not the absolute numbers.

Installing the Sentry React Native SDK

Sentry ships a wizard that patches the project for you:

npx @sentry/wizard@latest -i reactNative

As of the current docs it installs @sentry/react-native, adds @sentry/react-native/metro to metro.config.js, adds the @sentry/react-native/expo config plugin to app.json when it detects Expo, enables the Gradle build step that uploads Android source maps and debug symbols, wraps the “Bundle React Native code and images” Xcode phase for iOS source maps, adds an “Upload Debug Symbols to Sentry” build phase, runs pod install, and writes credentials to ios/sentry.properties, android/sentry.properties and .env.local. Commit the patched files and gitignore the credentials. Run the wizard once; rerunning it on an already patched project is how people end up with two Metro wrappers.

If you prefer to see every change, the manual route is short. Install the package, then wrap your Metro config:

// metro.config.js (React Native CLI)
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
const { withSentryConfig } = require('@sentry/react-native/metro');

const config = mergeConfig(getDefaultConfig(__dirname), {});
module.exports = withSentryConfig(config);
// metro.config.js (Expo)
const { getSentryExpoConfig } = require('@sentry/react-native/metro');
module.exports = getSentryExpoConfig(__dirname);

The Metro plugin injects the Debug ID that Sentry uses to pair a bundle with its source map. Without it, uploaded maps exist but never match, which is the single most common “my stack traces are still minified” ticket.

Expo projects

For Expo, add the config plugin and let prebuild do the native edits:

{
  "expo": {
    "plugins": [
      ["@sentry/react-native/expo", {
        "organization": "your-org",
        "project": "your-project",
        "url": "https://sentry.io/"
      }]
    ]
  }
}

Set SENTRY_AUTH_TOKEN as an EAS secret (or environment variable) so EAS Build can upload source maps and debug symbols. For over-the-air updates the bundle changes without a native build, so after npx expo export run npx sentry-expo-upload-sourcemaps dist, or let the eas update step do it when the plugin is configured. Our Expo guide covers where EAS secrets live if you have not used them.

Initialising Sentry

Call Sentry.init before anything renders, then wrap the root component:

// App.tsx
import * as Sentry from '@sentry/react-native';

const navigationIntegration = Sentry.reactNavigationIntegration({
  enableTimeToInitialDisplay: true,
});

Sentry.init({
  dsn: 'https://[email protected]/0',
  environment: __DEV__ ? 'development' : 'production',
  release: '[email protected]+87',
  dist: '87',
  sendDefaultPii: false,
  tracesSampleRate: __DEV__ ? 1.0 : 0.2,
  profilesSampleRate: 0.1,
  replaysSessionSampleRate: 0.05,
  replaysOnErrorSampleRate: 1.0,
  enableLogs: true,
  integrations: [
    navigationIntegration,
    Sentry.mobileReplayIntegration({
      maskAllText: true,
      maskAllImages: true,
      maskAllVectors: true,
    }),
    Sentry.feedbackIntegration(),
  ],
  beforeSend(event) {
    if (event.user?.email) delete event.user.email;
    return event;
  },
});

function App() {
  return (
     navigationIntegration.registerNavigationContainer(navigationRef)}
    >
      {/* screens */}
    
  );
}

export default Sentry.wrap(App);

Points that matter in production. release and dist must match what the build uploaded, or symbolication fails silently; the SDK derives sensible defaults from the native app version and build number, so only override them if you also override the upload. tracesSampleRate at 1.0 is fine in a demo and a bill in a store app with a hundred thousand daily users. Session Replay on mobile masks all text and images by default; keep it that way for anything with health, finance or chat content, and remember that 8.33.0 fixed Android not applying the screenshot masking options to error and feedback screenshots, so older SDKs leaked more than their config said. The 8.33.0 release also persists fatal JavaScript error envelopes to disk on Android before the process exits; before this, the event was queued on the transport thread and lost when React Native killed the process, which is why some teams saw iOS fatals and almost no Android ones.

Capturing errors with context

Uncaught JavaScript exceptions, unhandled promise rejections and native crashes are reported automatically. The work is in the handled cases:

import * as Sentry from '@sentry/react-native';

Sentry.setUser({ id: user.id }); // no email, no name unless you need them

Sentry.addBreadcrumb({
  category: 'chat',
  message: 'send message',
  level: 'info',
  data: { roomId, attachmentCount },
});

try {
  await sendMessage(roomId, payload);
} catch (err) {
  Sentry.captureException(err, {
    tags: { feature: 'chat', transport: 'websocket' },
    extra: { roomId, retry: attempt },
  });
  showToast('Message not sent, retrying');
}

Wrap screens that can fail independently in Sentry.ErrorBoundary with a fallback, so one broken list does not take the whole app down and the error still reaches the dashboard. Keep Sentry.setUser to an opaque id. If you hold personally identifiable data at all, that decision belongs in the same review as the rest of your React Native security checklist, not in a logging library’s defaults.

Source maps and native symbols

Hermes bytecode means a production stack trace is useless without the matching source map, and a native crash is useless without dSYMs (iOS) and ProGuard mappings plus native debug symbols (Android). The wizard sets all four uploads up. The things that break them later: a CI runner without SENTRY_AUTH_TOKEN, a custom release string that differs between upload and runtime, an OTA update shipped without re-uploading, and Metro configs that got rewritten during a React Native upgrade and lost the withSentryConfig wrapper. After every React Native upgrade, check that metro.config.js still wraps, trigger a test error, and confirm the frame shows your TypeScript. Thirty seconds, saves a week.

The alternatives

Firebase Crashlytics (@react-native-firebase/crashlytics 27.1.0)

Free, excellent at native crashes, and already present if the app uses other Firebase products. React Native Firebase moved to major version 27 on October 19, 2026 and patched to 27.1.0 four days later; pin the whole @react-native-firebase/* family to one version, always. JavaScript errors are recorded through crashlytics().recordError(), but there is no source map pipeline comparable to Sentry’s, no session replay, and no performance tracing in the same product (Firebase Performance is separate). Pick it when the budget is zero and the native crash is what you fear most.

Datadog (@datadog/mobile-react-native 3.11.0)

The right answer when the backend already lives in Datadog: RUM sessions, errors, logs and traces in one account, with the React Native SDK on a two-week release cadence through October. The error tracking is good, the price is set at the Datadog level, and the mobile-only team rarely chooses it on its own.

Bugsnag (@bugsnag/react-native 9.1.0)

SmartBear’s SDK moved to 9.x in September 2026. It has a long record on stability reporting and a clean “stability score” per release that product teams like. Fewer releases than Sentry (six in 2026 so far), smaller community, solid product. Worth a trial if Sentry’s per-event pricing does not fit your volume.

PostHog (posthog-react-native 4.85.1)

PostHog added error tracking to its product analytics and feature flag SDK, and the React Native package ships almost daily (4.84.0 to 4.85.1 in the week of October 26). If the app already sends events to PostHog, turning on exception capture costs one config flag. It is not a replacement for native crash reporting yet; treat it as a complement for JavaScript errors tied to product funnels.

EAS Observe (Expo, preview)

Expo’s observability service started with JavaScript errors and, since October 7, 2026, records native crashes on both platforms with no setup on SDK 57.0.21 and later: uncaught Java and Kotlin exceptions and the OS crash records on Android, Mach exceptions, Unix signals and uncaught Objective-C and Swift exceptions on iOS. Crashes list alongside JavaScript errors with the session records that led up to them, and a “Hand off to AI” button sends the crash to Claude Code or Codex. EAS CLI 27.2.0 added eas observe:traces for span trees from the terminal. It is in preview and tied to EAS; for an Expo app that already builds and updates through EAS, it is the lowest-friction option on this page and the one to watch.

Self-hosted: Sentry and GlitchTip

Sentry’s server is open source and can be self-hosted with Docker Compose, and GlitchTip speaks the Sentry SDK protocol with a smaller footprint. The React Native SDK works unchanged against either; only the DSN differs. Teams with data residency rules use this route, and it is where a lot of regulated-industry React Native apps actually end up.

react-native-exception-handler

Still downloaded fifty thousand times a week, last published March 2021, before the New Architecture. It sets global JavaScript and native exception handlers so you can show a custom “something went wrong” screen. It is not error tracking and it is not maintained. If it is in your package.json, replace it with Sentry.ErrorBoundary or Expo’s error boundaries.

Decision table

Situation Pick
Default for a new production app Sentry (wizard install, replay and tracing when you need them)
Zero budget, Firebase already in the app Crashlytics 27.x
Backend monitored in Datadog @datadog/mobile-react-native
Expo app on EAS, want the least setup EAS Observe (preview) now, Sentry alongside if you need SLAs
Data must stay on your servers Self-hosted Sentry or GlitchTip
Product analytics team owns the dashboard PostHog exception capture as a complement

FAQ

Does Sentry catch native crashes in React Native?

Yes. @sentry/react-native includes the iOS and Android Sentry SDKs, so uncaught native exceptions, signals and Mach exceptions are reported alongside JavaScript errors, with dSYM and ProGuard symbolication when the build uploads were configured.

Why are my Sentry stack traces minified?

Almost always a source map mismatch: the Metro plugin is missing so no Debug ID was injected, the release and dist differ between build and runtime, or an OTA update shipped without re-uploading maps. Fix the pairing, not the SDK.

Is Sentry free for React Native?

Sentry has a free developer tier with event limits, paid plans billed by volume, and an open-source server you can self-host. The SDK itself is MIT licensed.

Can I use Sentry with Expo Go?

The SDK works in development builds and EAS builds. Expo Go does not include the Sentry native module, so native crash reporting and some integrations are unavailable there; build a development client instead.

Sentry or Crashlytics for React Native?

Crashlytics is free and strong on native crashes; Sentry adds first-class JavaScript source maps, performance tracing, session replay and self-hosting. Most teams that can pay choose Sentry; teams on Firebase with no budget choose Crashlytics.

Leave a Reply 0

Your email address will not be published. Required fields are marked *