Skip to Content
Monitoring

Monitoring

NativeExpress reports crashes, errors and screen performance to Sentry  through a crash-reporting port in src/services/crash-reporting/.

The Sentry DSN enables error reporting. Source maps need build configuration and an upload token so stack traces point to your source files. Without a DSN the port is a no-op, and the integration can be removed entirely.

Prerequisites

Complete the app setup. For cloud builds, configure an EAS project and build environment.

Setup

Create a Sentry project

Sign up for Sentry  or sign in, then create a React Native project. Copy its DSN, organization slug and project slug.

Set the project values

Add these values to .env.local:

.env.local
EXPO_PUBLIC_SENTRY_DSN="<your-project-dsn>" EXPO_PUBLIC_SENTRY_URL="https://sentry.io/" EXPO_PUBLIC_SENTRY_PROJECT="<your-project-slug>" EXPO_PUBLIC_SENTRY_ORGANIZATION="<your-organization-slug>"

Use your Sentry server URL if you self-host. For EAS builds, add the same variables to the environment selected by your build profile.

app.config.js enables the source-map upload plugin only when the URL, project and organization are all set. The DSN alone enables runtime reporting.

Add an upload token

Create a Sentry auth token with the org:ci scope for source-map uploads, then add it to your EAS build environment:

eas env:set --name SENTRY_AUTH_TOKEN --value "<your-sentry-auth-token>" --environment production --visibility secret

Repeat this for any other environment you build with source maps. For local release builds, set SENTRY_AUTH_TOKEN in your shell environment.

Keep this token out of EXPO_PUBLIC_* variables and committed files. It authenticates the build’s upload to Sentry.

Build the app

Create a new production build. Adding the source-map plugin changes native configuration, so restarting the development server does not apply it.

See Expo’s Sentry guide  for the build integration.

Verify

Add a temporary button that calls the app’s logger:

import { logError } from '@/lib/logger'; logError('Sentry setup check', new Error('Sentry setup check'));

Run the button in the release build. Find the error in Sentry and confirm that the stack trace shows a source filename and line number, and that the User field holds the signed-in user’s id. Remove the temporary button after the check.

Then move between a few screens and open Traces in Sentry. Each screen appears as a navigation transaction named after its route.

What Sentry receives

  • Crashes and unhandled errors. This includes render errors that show a route’s error screen: metro.config.js wraps every export { ErrorBoundary } from 'expo-router' so it reports to Sentry. A route with its own ErrorBoundary component has to call logError itself.
  • Handled errors you pass to logError, from release builds only.
  • The signed-in user’s id. The email is never sent. The id is set on sign-in and cleared on sign-out.
  • Performance traces from every development session and 20% of release sessions: screen load time until first render, slow and frozen frames, and network request timings. Frame and render timings need a development or store build; Expo Go does not report them. To change the release rate, edit PRODUCTION_TRACES_SAMPLE_RATE in src/services/crash-reporting/sentry.adapter.ts.

Personal data stays off. sendDefaultPii is false, so request headers, route params and the device name are not sent. The adapter also strips query strings from request URLs, because Supabase puts row filters such as ?id=eq.<user-id> in them. The Sentry entries in privacy.config.json, which feed your App Store and Google Play privacy answers, describe this setup. If you turn on sendDefaultPii, Session Replay or Sentry Logs, update those entries too.

Reporting handled errors

Use logError(message, error) from @/lib/logger when a caught error needs reporting. In development it writes to the console. In release builds it hands the exception to the crash-reporting port, which forwards it to Sentry when EXPO_PUBLIC_SENTRY_DSN is set and drops it otherwise.

Show an error state or toast as well when the user needs to retry. Logging an exception does not display a message in the app.

src/services/crash-reporting/sentry.adapter.ts is the only module allowed to import @sentry/react-native; a lint rule rejects the import anywhere else. Report through logError rather than calling Sentry directly.

Your coding agent can help inspect reported issues through the Sentry tools.

Removing Sentry

yarn remove:integration sentry --dry-run yarn remove:integration sentry

The script deletes the adapter, wires the no-op into the port, and removes the config plugin, the Jest mock and the expo.install.exclude entry. metro.config.js uses Sentry’s Metro wrapper only when @sentry/react-native resolves, so it falls back to Expo’s default config on its own. logError keeps working. The script prints the yarn remove command to run afterwards. See Removing features and integrations.

Files

      • crash-reporting.port.ts - The port and its no-op
      • sentry.adapter.ts - Sentry.init, tracing and URL scrubbing
      • index.ts - Picks the adapter when the DSN is set
    • lib/logger.ts - logError
    • provider/SessionProvider.tsx - Sets and clears the Sentry user id
  • app.config.js - Registers the source-map plugin when all three plugin variables are set
  • metro.config.js - Sentry's Metro wrapper, which also wraps route error boundaries

Errors arrive with unreadable stack traces

Check that all three source-map plugin variables and SENTRY_AUTH_TOKEN are available in the build environment. Rebuild and repeat the test above. An error appearing in Sentry confirms reporting; readable source locations confirm that source maps uploaded.

Last updated on