Labsco
getsentry logo

sentry-react-native-sdk

β˜… 233

by getsentry Β· part of getsentry/sentry-for-claude

Full Sentry SDK setup for React Native and Expo. Use when asked to "add Sentry to React Native", "install @sentry/react-native", "setup Sentry in Expo", or configure error monitoring, tracing, profiling, session replay, or logging for React Native applications. Supports Expo managed, Expo bare, and vanilla React Native.

πŸ”Œ This skill ships inside the sentry plugin β€” install the plugin and you also get 1 slash commands, an MCP server.

This is the playbook your agent receives when the skill activates β€” you don't need to read it to use the skill, but it's here to audit before installing.

All Skills > SDK Setup > React Native SDK

Sentry React Native SDK

Opinionated wizard that scans your React Native or Expo project and guides you through complete Sentry setup β€” error monitoring, tracing, profiling, session replay, logging, and more.

Invoke This Skill When

  • User asks to "add Sentry to React Native" or "set up Sentry" in an RN or Expo app
  • User wants error monitoring, tracing, profiling, session replay, or logging in React Native
  • User mentions @sentry/react-native, mobile error tracking, or Sentry for Expo
  • User wants to monitor native crashes, ANRs, or app hangs on iOS/Android

Note: SDK versions and APIs below reflect current Sentry docs at time of writing (@sentry/react-native β‰₯6.0.0, minimum recommended β‰₯8.0.0). Always verify against docs.sentry.io/platforms/react-native/ before implementing.


Phase 1: Detect

Run these commands to understand the project before making any recommendations:

# Detect project type and existing Sentry
cat package.json | grep -E '"(react-native|expo|@expo|@sentry/react-native|sentry-expo)"'

# Distinguish Expo managed vs bare vs vanilla RN
ls app.json app.config.js app.config.ts 2>/dev/null
cat app.json 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); print('Expo managed' if 'expo' in d else 'Bare/Vanilla')" 2>/dev/null

# Check Expo SDK version (important: Expo SDK 50+ required for @sentry/react-native)
cat package.json | grep '"expo"'

# Detect navigation library
grep -E '"(@react-navigation/native|react-native-navigation)"' package.json

# Detect state management (Redux β†’ breadcrumb integration available)
grep -E '"(redux|@reduxjs/toolkit|zustand|mobx)"' package.json

# Check for existing Sentry initialization
grep -r "Sentry.init" src/ app/ App.tsx App.js _layout.tsx 2>/dev/null | head -5

# Detect Hermes (affects source map handling)
cat android/app/build.gradle 2>/dev/null | grep -i hermes
cat ios/Podfile 2>/dev/null | grep -i hermes

# Detect Expo Router
ls app/_layout.tsx app/_layout.js 2>/dev/null

# Detect backend for cross-link
ls backend/ server/ api/ 2>/dev/null
find . -maxdepth 3 \( -name "go.mod" -o -name "requirements.txt" -o -name "Gemfile" -o -name "package.json" \) 2>/dev/null | grep -v node_modules | head -10

What to determine:

QuestionImpact
expo in package.json?Expo path (config plugin + getSentryExpoConfig) vs bare/vanilla RN path
Expo SDK β‰₯50?@sentry/react-native directly; older = sentry-expo (legacy, do not use)
app.json has "expo" key?Managed Expo β€” wizard is simplest; config plugin handles all native config
app/_layout.tsx present?Expo Router project β€” init goes in _layout.tsx
@sentry/react-native already in package.json?Skip install, jump to feature config
@react-navigation/native present?Recommend reactNavigationIntegration for screen tracking
react-native-navigation present?Recommend reactNativeNavigationIntegration (Wix)
Backend directory detected?Trigger Phase 4 cross-link

Phase 2: Recommend

Present a concrete recommendation based on what you found. Don't ask open-ended questions β€” lead with a proposal:

Recommended (core coverage β€” always set up these):

  • βœ… Error Monitoring β€” captures JS exceptions, native crashes (iOS + Android), ANRs, and app hangs
  • βœ… Tracing β€” mobile performance is critical; auto-instruments navigation, app start, network requests
  • βœ… Session Replay β€” mobile replay captures screenshots and touch events for debugging user issues

Optional (enhanced observability):

  • ⚑ Profiling β€” CPU profiling on iOS (JS profiling cross-platform); low overhead in production
  • ⚑ Logging β€” structured logs via Sentry.logger.*; links to traces for full context
  • ⚑ User Feedback β€” collect user-submitted bug reports directly from your app

Recommendation logic:

FeatureRecommend when...
Error MonitoringAlways β€” non-negotiable baseline for any mobile app
TracingAlways for mobile β€” app start, navigation, and network latency matter
Session ReplayUser-facing production app; debug user-reported issues visually
ProfilingPerformance-sensitive screens, startup time concerns, or production perf investigations
LoggingApp uses structured logging, or you want log-to-trace correlation in Sentry
User FeedbackBeta or customer-facing app where you want user-submitted bug reports

Propose: "For your [Expo managed / bare RN] app, I recommend setting up Error Monitoring + Tracing + Session Replay. Want me to also add Profiling and Logging?"


Phase 3: Guide

Determine Your Setup Path

Project typeRecommended setupComplexity
Expo managed (SDK 50+)Wizard CLI or manual with config pluginLow β€” wizard does everything
Expo bare (SDK 50+)Wizard CLI recommendedMedium β€” handles iOS/Android config
Vanilla React Native (0.69+)Wizard CLI recommendedMedium β€” handles Xcode + Gradle
Expo SDK <50Use sentry-expo (legacy)See legacy docs

You need to run this yourself β€” the wizard opens a browser for login and requires interactive input that the agent can't handle. Copy-paste into your terminal:

npx @sentry/wizard@latest -i reactNative

It handles login, org/project selection, SDK installation, native config, source map upload, and Sentry.init(). Here's what it creates/modifies:

FileActionPurpose
package.jsonInstalls @sentry/react-nativeCore SDK
metro.config.jsAdds @sentry/react-native/metro serializerSource map generation
app.jsonAdds @sentry/react-native/expo plugin (Expo only)Config plugin for native builds
App.tsx / _layout.tsxAdds Sentry.init() and Sentry.wrap()SDK initialization
ios/sentry.propertiesStores org/project/tokeniOS source map + dSYM upload
android/sentry.propertiesStores org/project/tokenAndroid source map upload
android/app/build.gradleAdds Sentry Gradle pluginAndroid source maps + proguard
ios/[AppName].xcodeprojWraps "Bundle RN" build phase + adds dSYM uploadiOS symbol upload
.env.localSENTRY_AUTH_TOKENAuth token (add to .gitignore)

Once it finishes, come back and skip to Verification.

If the user skips the wizard, proceed with Path B or C (Manual Setup) below based on their project type.


Path B: Manual β€” Expo Managed (SDK 50+)

Step 1 β€” Install

npx expo install @sentry/react-native

Step 2 β€” metro.config.js

const { getSentryExpoConfig } = require("@sentry/react-native/metro");
const config = getSentryExpoConfig(__dirname, {
  // Auto-wrap Expo Router ErrorBoundary exports at build time (SDK β‰₯8.17.0)
  autoWrapExpoRouterErrorBoundary: true,
});
module.exports = config;

If metro.config.js doesn't exist yet:

npx expo customize metro.config.js
# Then replace contents with the above

Metro config options:

OptionTypeDefaultPurpose
autoWrapExpoRouterErrorBoundarybooleanfalseAutomatically wrap export { ErrorBoundary } from 'expo-router' with Sentry at build time (SDK β‰₯8.17.0). Captures errors that hit per-route ErrorBoundaries without manual wrapping
annotateReactComponentsbooleanfalseInject component names for better error context
includeWebReplaybooleantrueInclude web replay bundle (set false for native-only apps)
includeWebFeedbackbooleantrueInclude web feedback bundle (set false for native-only apps)
enableSourceContextInDevelopmentbooleantrueEnable source context in dev builds

Step 3 β€” app.json β€” Add Expo config plugin

{
  "expo": {
    "plugins": [
      [
        "@sentry/react-native/expo",
        {
          "url": "https://sentry.io/",
          "project": "YOUR_PROJECT_SLUG",
          "organization": "YOUR_ORG_SLUG",
          "disableAutoUpload": false
        }
      ]
    ]
  }
}

Note: Set SENTRY_AUTH_TOKEN as an environment variable for native builds β€” never commit it to version control.

Plugin options:

OptionTypeDefaultPurpose
urlstring"https://sentry.io/"Sentry instance URL
projectstringβ€”Project slug
organizationstringβ€”Organization slug
disableAutoUploadbooleanfalseSkip source map + dSYM upload during local builds (SDK β‰₯8.13.0)

Step 4 β€” Initialize Sentry

For Expo Router (app/_layout.tsx):

import { Stack } from "expo-router";
import { isRunningInExpoGo } from "expo";
import * as Sentry from "@sentry/react-native";

Sentry.init({
  dsn: process.env.EXPO_PUBLIC_SENTRY_DSN ?? "YOUR_SENTRY_DSN",
  sendDefaultPii: true,

  // Tracing
  tracesSampleRate: 1.0, // lower to 0.1–0.2 in production

  // Profiling
  profilesSampleRate: 1.0,

  // Session Replay
  replaysOnErrorSampleRate: 1.0,
  replaysSessionSampleRate: 0.1,

  // Logging (SDK β‰₯7.0.0)
  enableLogs: true,

  // Session Replay
  integrations: [
    Sentry.mobileReplayIntegration(),
  ],

  enableNativeFramesTracking: !isRunningInExpoGo(), // slow/frozen frames

  environment: __DEV__ ? "development" : "production",
});

function RootLayout() {
  return <Stack />;
}

export default Sentry.wrap(RootLayout);

Note: Expo Router automatically handles navigation tracking. The Sentry.NavigationContainer wrapper is not needed for Expo Router projects β€” navigation spans are captured automatically.

Expo Router ErrorBoundary Setup (SDK β‰₯8.16.0)

Expo Router's per-route ErrorBoundary swallows render errors by default β€” Sentry won't see them unless you explicitly capture them. Two options:

  1. Auto-wrap (recommended, SDK β‰₯8.17.0) β€” Enable autoWrapExpoRouterErrorBoundary: true in metro.config.js (shown above). The Babel plugin rewrites export { ErrorBoundary } from 'expo-router' automatically in all route files.

  2. Manual wrap β€” Wrap the boundary yourself in each route file:

    // app/_layout.tsx (or any route file)
    import { ErrorBoundary as ExpoErrorBoundary } from 'expo-router';
    import * as Sentry from '@sentry/react-native';
    
    export const ErrorBoundary = Sentry.wrapExpoRouterErrorBoundary(ExpoErrorBoundary);

Both methods capture errors that hit the boundary with route context (route.name, route.path, route.params), tag the active navigation span as errored, and emit a breadcrumb. Concrete paths/params respect sendDefaultPii.

For standard Expo (App.tsx):

import { isRunningInExpoGo } from "expo";
import * as Sentry from "@sentry/react-native";

Sentry.init({
  dsn: process.env.EXPO_PUBLIC_SENTRY_DSN ?? "YOUR_SENTRY_DSN",
  sendDefaultPii: true,
  tracesSampleRate: 1.0,
  profilesSampleRate: 1.0,
  replaysOnErrorSampleRate: 1.0,
  replaysSessionSampleRate: 0.1,
  enableLogs: true,
  integrations: [
    Sentry.mobileReplayIntegration(),
  ],
  enableNativeFramesTracking: !isRunningInExpoGo(),
  environment: __DEV__ ? "development" : "production",
});

function App() {
  return (
    <Sentry.NavigationContainer>
      {/* your navigation here */}
    </Sentry.NavigationContainer>
  );
}

export default Sentry.wrap(App);

Path C: Manual β€” Bare React Native (0.69+)

Step 1 β€” Install

npm install @sentry/react-native --save
cd ios && pod install

Step 2 β€” metro.config.js

const { getDefaultConfig } = require("@react-native/metro-config");
const { withSentryConfig } = require("@sentry/react-native/metro");

const config = getDefaultConfig(__dirname);
module.exports = withSentryConfig(config, {
  // Set to false to exclude @sentry-internal/replay from the native bundle (web only).
  // includeWebReplay: true,
  // Set to false to exclude @sentry-internal/feedback from the native bundle (web only).
  // includeWebFeedback: true,
  // Auto-wrap Expo Router ErrorBoundary exports at build time (SDK β‰₯8.17.0, Expo Router only)
  // autoWrapExpoRouterErrorBoundary: true,
});

Step 3 β€” iOS: Modify Xcode build phase

Open ios/[AppName].xcodeproj in Xcode. Find the "Bundle React Native code and images" build phase and replace the script content with:

# RN 0.81.1+
set -e
WITH_ENVIRONMENT="../node_modules/react-native/scripts/xcode/with-environment.sh"
SENTRY_XCODE="../node_modules/@sentry/react-native/scripts/sentry-xcode.sh"
/bin/sh -c "$WITH_ENVIRONMENT $SENTRY_XCODE"

Step 4 β€” iOS: Add "Upload Debug Symbols to Sentry" build phase

Add a new Run Script build phase in Xcode (after the bundle phase):

/bin/sh ../node_modules/@sentry/react-native/scripts/sentry-xcode-debug-files.sh

Step 5 β€” iOS: ios/sentry.properties

defaults.url=https://sentry.io/
defaults.org=YOUR_ORG_SLUG
defaults.project=YOUR_PROJECT_SLUG
auth.token=YOUR_ORG_AUTH_TOKEN

Step 6 β€” Android: android/app/build.gradle

Add before the android {} block:

apply from: "../../node_modules/@sentry/react-native/sentry.gradle.kts"

Note: SDK β‰₯8.13.0 uses sentry.gradle.kts (Kotlin DSL). For older SDKs, use sentry.gradle (Groovy). Both are backward-compatible.

Step 7 β€” Android: android/sentry.properties

defaults.url=https://sentry.io/
defaults.org=YOUR_ORG_SLUG
defaults.project=YOUR_PROJECT_SLUG
auth.token=YOUR_ORG_AUTH_TOKEN

Step 8 β€” Initialize Sentry (App.tsx or entry point)

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

Sentry.init({
  dsn: "YOUR_SENTRY_DSN",
  sendDefaultPii: true,
  tracesSampleRate: 1.0,
  profilesSampleRate: 1.0,
  replaysOnErrorSampleRate: 1.0,
  replaysSessionSampleRate: 0.1,
  enableLogs: true,
  integrations: [
    Sentry.mobileReplayIntegration(),
  ],
  enableNativeFramesTracking: true,
  environment: __DEV__ ? "development" : "production",
});

function App() {
  return (
    <Sentry.NavigationContainer>
      {/* your navigation here */}
    </Sentry.NavigationContainer>
  );
}

export default Sentry.wrap(App);

This is the recommended starting configuration with all features enabled:

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

Sentry.init({
  dsn: "YOUR_SENTRY_DSN",
  sendDefaultPii: true,

  // Tracing β€” lower to 0.1–0.2 in high-traffic production
  tracesSampleRate: 1.0,

  // Profiling β€” runs on a subset of traced transactions
  profilesSampleRate: 1.0,

  // Session Replay β€” always capture on error, sample 10% of all sessions
  replaysOnErrorSampleRate: 1.0,
  replaysSessionSampleRate: 0.1,

  // Logging β€” enable Sentry.logger.* API
  enableLogs: true,

  // Integrations β€” mobile replay is opt-in
  integrations: [
    Sentry.mobileReplayIntegration({
      maskAllText: true,   // masks text by default for privacy
      maskAllImages: true,
    }),
  ],

  // Native frames tracking (disable in Expo Go)
  enableNativeFramesTracking: true,

  // Environment
  environment: __DEV__ ? "development" : "production",

  // Release β€” set from CI or build system
  // release: "my-app@1.0.0+1",
  // dist: "1",
});

// REQUIRED: Wrap root component to capture React render errors
export default Sentry.wrap(App);

App Start Accuracy β€” Sentry.appLoaded() (SDK β‰₯8.x)

If your app does significant async work after the root component mounts (e.g., fetching config, waiting for auth), call Sentry.appLoaded() once that work is complete. This signals the true end of app startup to Sentry and produces more accurate app start duration measurements.

// Call after async initialization is complete, e.g., in a useEffect or after a loading screen:
useEffect(() => {
  fetchConfig().then(() => {
    Sentry.appLoaded();  // marks the end of the app startup phase
  });
}, []);

If you don't call Sentry.appLoaded(), the SDK estimates the app start end automatically.


Recommended: Use Sentry.NavigationContainer wrapper (SDK β‰₯8.13.0)

Drop-in replacement for NavigationContainer that automatically wires up navigation tracking:

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

// Replace NavigationContainer with Sentry.NavigationContainer
<Sentry.NavigationContainer>
  <Stack.Navigator>
    {/* your screens */}
  </Stack.Navigator>
</Sentry.NavigationContainer>

That's it! The wrapper automatically:

  • Creates the reactNavigationIntegration
  • Registers the navigation container ref
  • Captures breadcrumbs for navigation events (SDK β‰₯8.13.0)
  • Tracks Time to Initial Display (TTID) per screen

Alternative: Manual setup (for SDK <8.13.0 or custom config)

import { reactNavigationIntegration } from "@sentry/react-native";
import { NavigationContainer, createNavigationContainerRef } from "@react-navigation/native";

const navigationIntegration = reactNavigationIntegration({
  enableTimeToInitialDisplay: true,   // track TTID per screen
  routeChangeTimeoutMs: 1_000,        // max wait for route change to settle
  ignoreEmptyBackNavigationTransactions: true,
});

// Add to Sentry.init integrations array
Sentry.init({
  integrations: [navigationIntegration],
  // ...
});

// In your component:
const navigationRef = createNavigationContainerRef();

<NavigationContainer
  ref={navigationRef}
  onReady={() => {
    navigationIntegration.registerNavigationContainer(navigationRef);
  }}
>
import * as Sentry from "@sentry/react-native";
import { Navigation } from "react-native-navigation";

Sentry.init({
  integrations: [Sentry.reactNativeNavigationIntegration({ navigation: Navigation })],
  // ...
});

Wrap Your Root Component

Always wrap your root component β€” this enables React error boundaries and ensures crashes at the component tree level are captured:

export default Sentry.wrap(App);

For Each Agreed Feature

Walk through features one at a time. Load the reference file for each, follow its steps, then verify before moving on:

FeatureReferenceLoad when...
Error Monitoring${SKILL_ROOT}/references/error-monitoring.mdAlways (baseline)
Tracing & Performance${SKILL_ROOT}/references/tracing.mdAlways for mobile (app start, navigation, network)
Profiling${SKILL_ROOT}/references/profiling.mdPerformance-sensitive production apps
Session Replay${SKILL_ROOT}/references/session-replay.mdUser-facing apps
Logging${SKILL_ROOT}/references/logging.mdStructured logging / log-to-trace correlation
User Feedback${SKILL_ROOT}/references/user-feedback.mdCollecting user-submitted reports
Expo Config Plugin${SKILL_ROOT}/references/expo-config-plugin.mdConfiguring the @sentry/react-native/expo plugin

For each feature: Read ${SKILL_ROOT}/references/<feature>.md, follow steps exactly, verify it works.


Verification

After setup, test that Sentry is receiving events:

// Quick test β€” throws and Sentry.wrap(App) catches it
<Button
  title="Test Sentry Error"
  onPress={() => {
    throw new Error("My first Sentry error!");
  }}
/>

// Or capture manually
<Button
  title="Test Sentry Message"
  onPress={() => {
    Sentry.captureMessage("Sentry test message", "info");
  }}
/>

Check the Sentry dashboard:

  • Issues β†’ your test error should appear within seconds
  • Traces β†’ look for a "main" transaction with child spans
  • Replays β†’ session recording visible after app interaction (native build only β€” not Expo Go)
  • Logs β†’ structured log entries if enableLogs: true

⚠️ Expo Go limitation: Native crashes, session replay, slow/frozen frames, TTID, and TTFD only work in native builds (eas build or expo run). Expo Go runs in a JS-only mode. Set enableNativeFramesTracking: !isRunningInExpoGo() to avoid warnings.

If nothing appears:

  1. Set debug: true β€” SDK logs to Metro console
  2. Verify DSN is correct and the Sentry project exists
  3. Ensure Sentry.wrap(App) is applied to the root component
  4. For native crashes, build a release build (crashes in debug mode may not forward)

After completing React Native setup, check for a backend or web frontend missing Sentry coverage:

# Adjacent backend directories
ls ../backend ../server ../api 2>/dev/null
cat ../go.mod 2>/dev/null | head -3
cat ../requirements.txt ../pyproject.toml 2>/dev/null | head -3
cat ../Gemfile 2>/dev/null | head -3
ls ../backend/package.json ../server/package.json 2>/dev/null

# Web frontend in same or sibling repo
ls ../web ../frontend ../dashboard 2>/dev/null
cat ../web/package.json ../frontend/package.json 2>/dev/null | grep -E '"react"|"svelte"|"next"'

# Check for OpenTelemetry (for distributed tracing interop)
grep -r "opentelemetry" ../backend/go.mod ../server/requirements.txt 2>/dev/null

If a backend or web frontend exists without Sentry, suggest the matching skill:

DetectedSuggest skill
Go backend (go.mod)sentry-go-sdk
Python backend (requirements.txt, pyproject.toml)sentry-python-sdk
Ruby backend (Gemfile)sentry-ruby-sdk
Node.js backend (Express, Fastify, etc.)@sentry/node β€” see docs.sentry.io/platforms/javascript/guides/express/
React / Next.js websentry-react-sdk
Svelte / SvelteKit websentry-svelte-sdk

Distributed tracing setup β€” if the backend skill is added, configure tracePropagationTargets in React Native to propagate trace context to your API:

Sentry.init({
  tracePropagationTargets: [
    "localhost",
    /^https:\/\/api\.yourapp\.com/,
  ],
  // ...
});

This links mobile transactions to backend traces in the Sentry waterfall view.