Capture App Start Errors
Learn how to capture app start errors and crashes that occur before JavaScript loads using native initialization.
By default, the React Native SDK initializes the native SDK underneath the init method called on the JS layer. As a result, the SDK has a current limitation of not capturing native crashes that occur prior to the init method being called on the JS layer.
Starting with SDK version 8.0.0, you can initialize Sentry natively before JavaScript loads, enabling capture of app start errors and crashes that occur during:
- Native module initialization
- JavaScript bundle loading
- Early React Native bridge setup
This feature uses a sentry.options.json configuration file and native initialization APIs that read from this file.
SDK Version Requirement
This feature requires Sentry React Native SDK version 8.0.0 or higher.
Create a sentry.options.json file in your React Native project root with the same options you currently have in Sentry.init:
sentry.options.json{
"dsn": "https://key@example.io/value",
"debug": true,
"environment": "production",
"tracesSampleRate": 1.0,
"enableTracing": true
}
{
"dsn": "https://key@example.io/value",
"debug": true,
"environment": "production",
"tracesSampleRate": 1.0,
"enableTracing": true
}
Options Merging
When Sentry.init() runs in JavaScript, the native SDK is re-initialized with the JS options merged on top of the file options. This means JavaScript options take precedence for events captured after JS loads.
However, crashes and errors that occur before JavaScript loads (which is the purpose of this feature) are captured using only the values from sentry.options.json and native auto-detection. If you set a custom release or dist in Sentry.init(), make sure the same values are also in sentry.options.json. Otherwise, pre-JS crashes will be attributed to a different release than post-JS events.
If you need different environment values for build (e.g., production vs staging), set the SENTRY_ENVIRONMENT environment variable at build time. The SDK build scripts will use this to override the environment in your sentry.options.json without modifying the source file.
SENTRY_ENVIRONMENT=staging npx react-native run-android
SENTRY_ENVIRONMENT=staging npx react-native run-android
This works in any CI/CD system by setting the environment variable in your build configuration.
If you use a custom release or dist in Sentry.init(), you should set matching values in sentry.options.json so that pre-JavaScript crashes are attributed to the correct release:
sentry.options.json{
"dsn": "https://key@example.io/value",
"release": "my-app@1.0.0+42",
"dist": "42"
}
{
"dsn": "https://key@example.io/value",
"release": "my-app@1.0.0+42",
"dist": "42"
}
For dynamic values that change per build, set the SENTRY_RELEASE and SENTRY_DIST environment variables at build time. The SDK build scripts will use these to override the values in your sentry.options.json, similar to how SENTRY_ENVIRONMENT works.
SENTRY_RELEASE="my-app@1.0.0+42" SENTRY_DIST="42" npx react-native run-ios
SENTRY_RELEASE="my-app@1.0.0+42" SENTRY_DIST="42" npx react-native run-ios
Release Alignment
If the release or dist in sentry.options.json doesn't match what you pass to Sentry.init(), you'll see two separate releases in Sentry — one for native crashes captured before JS loads and another for events captured after. Make sure both sources use the same values.
Initialize Sentry in your MainApplication class:
import io.sentry.react.RNSentrySDK
class MainApplication : Application(), ReactApplication {
override fun onCreate() {
super.onCreate()
RNSentrySDK.init(this)
// ... rest of your initialization code
}
}
import io.sentry.react.RNSentrySDK
class MainApplication : Application(), ReactApplication {
override fun onCreate() {
super.onCreate()
RNSentrySDK.init(this)
// ... rest of your initialization code
}
}
import io.sentry.react.RNSentrySDK;
public class MainApplication extends Application implements ReactApplication {
@Override
public void onCreate() {
super.onCreate();
RNSentrySDK.init(this);
// ... rest of your initialization code
}
}
Initialize Sentry in your AppDelegate before starting React Native so app start crashes are captured.
#import <RNSentry/RNSentry.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[RNSentrySDK start];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end
#import <RNSentry/RNSentry.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[RNSentrySDK start];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end
import RNSentry
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
RNSentrySDK.start()
// ... rest of your initialization code
return true
}
}
If you're using Expo, you can enable native initialization using the Expo plugin. Add the useNativeInit option and your SDK options to the plugin configuration:
app.json{
"expo": {
"plugins": [
[
"@sentry/react-native/expo",
{
"useNativeInit": true,
"options": {
"dsn": "https://key@example.io/value",
"environment": "production",
"tracesSampleRate": 1.0
}
}
]
]
}
}
{
"expo": {
"plugins": [
[
"@sentry/react-native/expo",
{
"useNativeInit": true,
"options": {
"dsn": "https://key@example.io/value",
"environment": "production",
"tracesSampleRate": 1.0
}
}
]
]
}
}
The plugin generates a sentry.options.json file from the options property during expo prebuild. If a sentry.options.json file already exists in your project root, the plugin options are merged into it (plugin options take precedence).
If you need to use environment variables or dynamic configuration, you can use app.config.js with the withSentry() wrapper instead of the array form in app.json:
app.config.jsimport { withSentry } from "@sentry/react-native/expo";
export default withSentry(config, {
useNativeInit: true,
options: {
dsn: "https://key@example.io/value",
environment: "production",
tracesSampleRate: 1.0,
},
});
import { withSentry } from "@sentry/react-native/expo";
export default withSentry(config, {
useNativeInit: true,
options: {
dsn: "https://key@example.io/value",
environment: "production",
tracesSampleRate: 1.0,
},
});
When useNativeInit is set to true, the Expo plugin automatically:
- Adds
RNSentrySDK.init()to your AndroidMainApplication - Adds
RNSentrySDK.start()to your iOSAppDelegate
You can set the environment using the plugin options property as shown above, or using the SENTRY_ENVIRONMENT environment variable. The environment variable takes precedence over the plugin option.
For per-environment builds with EAS Build, set SENTRY_ENVIRONMENT in your build profiles. You can also set SENTRY_RELEASE and SENTRY_DIST if you use custom release names:
eas.json{
"build": {
"production": {
"env": {
"SENTRY_ENVIRONMENT": "production",
"SENTRY_RELEASE": "my-app@1.0.0+42",
"SENTRY_DIST": "42"
}
},
"staging": {
"env": {
"SENTRY_ENVIRONMENT": "staging"
}
}
}
}
{
"build": {
"production": {
"env": {
"SENTRY_ENVIRONMENT": "production",
"SENTRY_RELEASE": "my-app@1.0.0+42",
"SENTRY_DIST": "42"
}
},
"staging": {
"env": {
"SENTRY_ENVIRONMENT": "staging"
}
}
}
}
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").