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:
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 secretRepeat 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.jswraps everyexport { ErrorBoundary } from 'expo-router'so it reports to Sentry. A route with its ownErrorBoundarycomponent has to calllogErroritself. - 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_RATEinsrc/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 sentryThe 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.