Skip to main content

Sentry React Native: 2026 Integration Guide

Integrate sentry react native from start to finish with our 2026 guide. Covers setup, native crashes, source maps, performance, and Capgo integration for

Martin Donadieu

Martin Donadieu

Content Marketer

Sentry React Native: 2026 Integration Guide

You’ve got a React Native app working locally, QA has signed off, and production is close. Then the obvious question lands: what happens when it breaks on a user’s device?

Without Sentry, the answer is usually bad. You get a support ticket, a vague screenshot, maybe a console log from a dev build that doesn’t match production. With Sentry React Native set up properly, you get the error, the stack, the release that shipped it, and enough context to fix it without guessing. The catch is that basic installation is the easy part. The painful parts come later: native integration, symbolication, source maps, release naming, and keeping all of that aligned when your delivery model includes live updates.

Most guides stop too early. A real setup has to survive CI, App Store builds, Android releases, and JavaScript bundles that don’t always come from the original binary.

Table of Contents

Getting Started with the Sentry SDK

The fastest way to get Sentry React Native into a new app is still the install wizard. It handles most of the repetitive setup and gets you to a working baseline quickly. That matters, because hand-rolling the first install usually leads to tiny mismatches that you won’t notice until the first production crash.

What you need before installing

You need a normal React Native dev environment first. Node, a package manager, platform tooling for iOS and Android, and Watchman on macOS if that’s already part of your workflow. You also need a Sentry account and a project created for React Native.

If you’re still evaluating whether React Native is the right operational choice for your team, this React Native guide for businesses gives useful non-marketing context around platform trade-offs, staffing, and maintenance expectations. It’s worth reading before you commit your monitoring and release process around a shared codebase.

Install the SDK with the wizard from the project root:

npx @sentry/wizard@latest -i reactNative

The wizard asks a few things that developers often click through too quickly:

  • Project selection. Pick the actual Sentry project you intend to use in production, not a temporary sandbox you’ll forget to update later.
  • Native changes. Say yes. JavaScript-only error capture is not enough for mobile apps.
  • Optional features. Turn on what you know you’ll use, but don’t enable everything blindly on day one if your team won’t review the resulting data.

Running the wizard and reviewing the result

After the wizard finishes, inspect the changes instead of trusting them without review. You should see a Sentry package in package.json, native changes under ios and android, and an initialization block in your app entry file.

A typical initialization looks like this:

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

Sentry.init({
  dsn: 'YOUR_DSN',
});

The DSN tells the SDK where to send events. Treat it as configuration, not as a secret vault item that must never be visible. It isn’t the same as an auth token. Still, keep your environment setup clean and consistent so your app points to the right Sentry project in each environment.

A practical pattern is to load the DSN from environment-specific config and initialize Sentry before the rest of your app tree mounts. If you’re also working through startup polish, this guide to React Native splash screen setup is useful because startup code order often intersects with where teams place Sentry initialization.

Practical rule: initialize Sentry as early as possible in app startup. If you wait until after navigation, auth hydration, or remote config, you’ll miss startup failures.

At this stage, don’t chase perfection. The immediate goal is simple: launch the app, trigger a JavaScript exception later in the article, and confirm events reach Sentry. Once that’s working, the native and release layers become much easier to reason about.

Configuring Native iOS and Android Projects

At this stage, many React Native teams get a false sense of completion. The JavaScript SDK is installed, events show up, and everyone assumes crash reporting is done. It isn’t. If native integration is off, some of the crashes you care about most will never reach Sentry in a usable form.

What changed on iOS

Open the iOS project and review what the wizard changed. In a bare React Native app, that usually means updates around app startup and build phases. You’re looking for Sentry initialization hooks and upload steps tied to your build process.

In Xcode, check these places:

  • App delegate startup code. The app needs native Sentry initialization early in launch.
  • Build Phases. Look for any Sentry upload script related to debug symbols or source map handling.
  • Build settings and archive behavior. Symbol files must be generated and available during archive builds.

If your app uses AppDelegate.mm, the initialization often sits close to the React Native bridge bootstrapping. The exact file content can vary by React Native version, template, and whether you’re using the new architecture, so don’t copy snippets from random repos unless they match your project shape.

What matters is the intent: iOS native crashes need symbol data, and the app has to start Sentry before the crash can be observed reliably.

If iOS crashes appear in Sentry with unreadable native frames, the problem usually isn’t “Sentry is broken.” It’s symbol upload or release matching.

What changed on Android

Android usually adds changes in Gradle files and sometimes manifest-level configuration. Review android/build.gradle, android/app/build.gradle, and any Sentry-related plugin or task wiring.

Things to verify:

  1. The Sentry Gradle plugin is applied so release artifacts can be processed during build time.
  2. Variant handling is sane if you use product flavors or multiple build types.
  3. ProGuard or R8 outputs are accounted for if your release builds shrink or obfuscate code.

A common Android mistake is assuming a successful local debug run proves the release setup is correct. It doesn’t. The release path is different, especially once minification and CI signing enter the picture. If your team maintains separate debug, staging, QA, and store builds, this breakdown of mobile build types is a useful reference for keeping monitoring behavior aligned with each build variant.

Native setup checks that save time later

Don’t stop at “the wizard modified files.” Verify behavior directly.

Use this checklist:

  • Archive an iOS build locally and confirm the build doesn’t fail during symbol processing.
  • Create a release Android build and inspect CI logs for Sentry-related tasks.
  • Check the package name and bundle identifier mapping in Sentry if you manage multiple apps under one org.
  • Confirm release naming conventions now, before CI starts uploading artifacts under inconsistent names.

Here’s what usually does not work well:

Approach What goes wrong
Trusting the wizard without review Native setup drifts when React Native or build tooling changes
Testing only in debug mode Debug success hides release-time symbolication issues
Mixing manual and automated upload steps Artifacts land under different releases and won’t match events

The best setup is boring. Native startup hooks are in place, build scripts run every time, and release naming is deterministic across iOS, Android, and JavaScript bundles.

Automating Releases and Source Maps

If there’s one place where Sentry React Native setups fall apart, it’s here. Teams install the SDK, see events, and postpone release automation. Then the first serious production issue comes in and the stack trace is minified, the release is missing, or the source map upload belonged to a different bundle.

Manual source map uploads sound acceptable when you’re shipping rarely. In practice, they fail because humans are bad at repetitive release bookkeeping.

Why manual uploads fail in practice

The failure modes are predictable:

  • Someone forgets to upload maps after a late-night hotfix.
  • The uploaded files belong to a different commit than the binary or OTA bundle users run.
  • The release name changes slightly between iOS, Android, and CI steps.
  • A rebuild happens after map upload and invalidates what Sentry should be matching against.

That’s why I don’t recommend a “document the steps in Notion” approach. It works until one urgent release goes out under pressure.

A seven-step flowchart illustrating the automated process of managing Sentry releases and source maps for React Native.

A release process that actually holds up

A reliable setup has a few properties:

  • Release IDs are generated once and reused everywhere.
  • Build, bundle, and upload steps happen in the same pipeline.
  • Source maps are uploaded from CI, not from a developer laptop.
  • The app initializes Sentry with the same release string that CI used during upload.

That last point matters more than commonly expected. You don’t just need source maps in Sentry. You need the correct source maps attached to the exact release identifier emitted by the app at runtime.

If your team is already standardizing mobile automation, this guide to automatic build and release workflows with GitHub Actions fits well with the same operational model.

A practical CI script pattern

Use a script like this in CI and feed values from your pipeline environment:

#!/usr/bin/env bash
set -euo pipefail

export SENTRY_AUTH_TOKEN="$SENTRY_AUTH_TOKEN"
export SENTRY_ORG="your-org"
export SENTRY_PROJECT="your-project"

RELEASE_NAME="${APP_VERSION}+${GIT_SHA}"

npx sentry-cli releases new "$RELEASE_NAME"

npx react-native bundle \
  --platform ios \
  --dev false \
  --entry-file index.js \
  --bundle-output ./dist/main.jsbundle \
  --sourcemap-output ./dist/main.jsbundle.map

npx sentry-cli releases files "$RELEASE_NAME" upload-sourcemaps ./dist \
  --rewrite \
  --strip-prefix "$(pwd)"

npx sentry-cli releases finalize "$RELEASE_NAME"

You’ll need to adapt the bundle command for Android, and many teams split platform-specific jobs instead of forcing one script to do both. That’s fine. What matters is consistency.

Release discipline beats clever scripting. Pick one naming convention, inject it into the app at build time, and never let local ad hoc uploads compete with CI.

For React Native, I prefer storing the release string in one build-generated config location and reading it during Sentry.init():

Sentry.init({
  dsn: Config.SENTRY_DSN,
  release: Config.SENTRY_RELEASE,
  dist: Config.SENTRY_DIST,
});

The payoff is simple. When an event arrives, Sentry can map the minified frame back to the code you shipped, not the code you think you shipped.

Capturing Performance Data and Custom Events

Crashes tell you what broke. Performance tracing tells you what users felt before they gave up.

A common report sounds like this: “The dashboard is slow.” That’s not enough to debug. Slow where? On navigation? During data fetch? While rendering a heavy chart? Sentry becomes useful here when you stop treating it like an error inbox and start instrumenting app behavior.

A software developer coding on a laptop with data visualization graphs displayed on a background monitor.

Tracing a slow screen instead of guessing

Start by enabling performance tracing in your initialization. The exact sampling strategy depends on your environment and volume tolerance, but the structure looks like this:

Sentry.init({
  dsn: Config.SENTRY_DSN,
  tracesSampleRate: 1.0,
});

If you use React Navigation, wire the integration so screen transitions produce trace data. Then reproduce the complaint on a physical device, not just a simulator. Simulators hide the kind of sluggishness users notice.

A practical dashboard example:

  1. A user opens the main dashboard after login.
  2. Navigation completes, but content appears late.
  3. The trace shows the screen transaction is long.
  4. Child spans reveal one API request and one expensive render path.
  5. You optimize the render path, ship again, and compare the new trace shape.

That’s better than arguing from gut feel.

For teams thinking broadly about webview or hybrid app monitoring patterns, this write-up on performance monitoring in Capacitor projects is worth scanning because the operational mindset is similar even though the stack differs.

Adding useful context to errors

Performance data gets more useful when events carry business context. Not vanity metadata. Just enough to answer who was affected, what screen they were on, and what happened right before failure.

Use these tools deliberately:

  • User context with Sentry.setUser() so support can correlate reports to an affected account without digging through guesswork.
  • Breadcrumbs for actions like tapping submit, opening a modal, or starting a sync.
  • Custom tags for dimensions like plan type, feature flag state, or API region.
  • Captured exceptions with extra context when you catch and rethrow or surface controlled failures.

Example:

Sentry.setUser({
  id: user.id,
  email: user.email,
});

Sentry.addBreadcrumb({
  category: 'navigation',
  message: 'Opened dashboard screen',
  level: 'info',
});

try {
  await loadDashboard();
} catch (error) {
  Sentry.captureException(error, {
    tags: { screen: 'dashboard' },
    extra: { widget: 'balance-summary' },
  });
}

A breadcrumb trail is often the difference between “user says the app froze” and “the app failed after opening dashboard, starting sync, and retrying a stale request.”

When custom instrumentation goes wrong, it usually goes wrong by being too noisy. Don’t capture every button press in the app forever. Capture boundaries, state transitions, and operations that matter when debugging. Enough context to explain the event. Not enough to drown it.

Verifying and Troubleshooting Your Integration

You should verify Sentry before shipping, after build pipeline changes, and after SDK upgrades. “It worked months ago” isn’t a meaningful test.

The cleanest way is to trigger controlled failures for both JavaScript and native paths, then inspect how they arrive in Sentry.

A male software developer writing code on a computer monitor with a test integration checklist on his desk.

Triggering test events safely

For a JavaScript error, add a temporary button in a non-production screen:

<Button
  title="Trigger JS Error"
  onPress={() => {
    throw new Error('Test JavaScript Sentry error');
  }}
/>

For a captured exception that won’t crash the app:

<Button
  title="Capture Exception"
  onPress={() => {
    Sentry.captureException(new Error('Handled Sentry test error'));
  }}
/>

Native crash testing should be done carefully and only in development or controlled QA builds. The exact helper methods available can vary by SDK version and platform wiring, so I prefer using the SDK’s documented native crash test utility when present rather than inventing your own crash path.

What to check in the Sentry UI

When the event appears, inspect more than the title.

Check these fields:

  • Platform and mechanism. This helps distinguish JS exceptions from native crashes.
  • Release and dist. If they’re blank or wrong, source maps and symbolication will drift.
  • Stack frames. Readable source locations should appear for properly uploaded JavaScript maps.
  • Breadcrumbs and tags. Confirm your custom context arrived.
  • Environment. Make sure development and production events aren’t mixed into one stream.

If a native event arrives but has poor symbolication, don’t keep tweaking app code. That’s usually a build artifact problem.

Common Sentry React Native Troubleshooting

Symptom Likely Cause Solution
JavaScript errors arrive, but stack traces are minified Source maps weren’t uploaded for the matching release Verify CI uploads maps after bundling and that release in Sentry.init() matches the uploaded release exactly
Native crashes don’t appear Native SDK hooks are missing or not initialized early enough Recheck iOS and Android native setup, then test with a controlled native crash path in a QA build
iOS native frames are unreadable Debug symbols were not uploaded or not linked to the right build Confirm archive builds generate symbols and that upload steps run during CI or Xcode archive flow
Android release behavior differs from debug Shrinking or obfuscation changes the release artifact path Review release Gradle tasks and verify Sentry processing runs for release variants
Events show up under the wrong environment Build-time config is leaking between environments Separate DSN, environment, release, and dist values per build target
Breadcrumbs or user data are missing Context is set too late or cleared during app state changes Set user and tags immediately after auth state resolves, and add breadcrumbs around critical flows

A final habit that pays off is keeping a tiny “monitoring smoke test” checklist in your release process. Trigger one JS event in staging, confirm release values, and verify source locations before promoting a build.

Integrating with Live Update Workflows like Capgo

Live updates change the release model. The binary in the store might stay the same while the JavaScript bundle changes underneath it. If Sentry still thinks only in terms of the original app version, stack traces become misleading fast.

The fix is to make Sentry release identity follow the live bundle, not just the native binary.

Matching release identifiers to live bundles

For live update workflows, treat release and dist as runtime identifiers tied to the delivered JavaScript package. The native app version still matters, but it isn’t enough on its own once bundles can change independently.

A practical pattern looks like this:

  • Use the native app version as part of the base release name.
  • Append the live update version or package identifier.
  • Use dist for channel or build-specific differentiation when that fits your model.
  • Upload source maps for each live bundle under that exact release identifier.

For example, if your app loads update metadata at startup, initialize Sentry with values derived from the currently active bundle, not just from static build config.

Sentry.init({
  dsn: Config.SENTRY_DSN,
  release: activeBundle.releaseName,
  dist: activeBundle.channel,
});

That way, when a user hits an error on a hotfixed bundle, Sentry resolves frames against the source map for that hotfix instead of the older store bundle.

This matters with any OTA-style workflow. If you want a good primer on the moving pieces behind that delivery model, this explanation of how live updates work in Capacitor apps is a solid reference.

Here’s the kind of operational view teams aim for when they combine update metadata with release tracking:

Screenshot from https://capgo.app

The main mistake to avoid is reusing one static release string for every post-store update. If multiple bundles share the same Sentry release, debugging turns into guesswork again.


If your team ships fixes outside app store review cycles, Capgo is worth evaluating. It gives Capacitor teams a structured way to deliver live updates, target channels, control rollouts, and recover from bad releases quickly. Pair that with disciplined Sentry release naming and source map uploads, and you get a workflow where errors still point to the exact code users are running.

Live updates for Capacitor apps

When a web-layer bug is live, ship the fix through Capgo instead of waiting days for app store approval. Users get the update in the background while native changes stay in the normal review path.

Get Started Now

Latest from our Blog

Capgo gives you the best insights you need to create a truly professional mobile app.