Sentry Setup
How to set up Sentry error reporting in your Caracal app.
Sentry is one of the most popular solutions for error reporting in the JavaScript ecosystem and has a great integration with Expo. The starter kit does not come with Sentry pre-configured, but it is very easy to set up and this guide will walk you through the process.
Install and configure Sentry
Create a new Sentry account if you don't have one already. Once logged in, create a new project for your React Native app.
During project creation, pay close attention to and note down the following important details:
- Organization slug
- Project name
- DSN
These details will be used next to configure the Sentry SDK in your app.
Generate a new Auth Token so you can use it to upload source maps to Sentry. Go to Developer Settings > Auth Tokens and create a new token.
Copy and securely store the generated token. You'll need this for configuring source map uploads.
At this point, you should have the following environment variables that need to be added to your .env files:
SENTRY_ORG=your_sentry_organization_slug
SENTRY_PROJECT=your_sentry_project_name
SENTRY_DSN=your_sentry_dsnYou can use the same Sentry configuration for all app variants (development, staging, production) as Sentry allows you to filter errors by app ID or package name in the dashboard. This simplifies setup and management while still providing the ability to distinguish between different environments.
It's crucial to add these variables to env.js for validation. SENTRY_ORG and SENTRY_PROJECT should be added as build-time variables, while SENTRY_DSN should be added as a client variable.
Update your env.js file as follows:
// ... existing imports and configurations
const client = z.object({
// ... other client env vars
SENTRY_DSN: z.string().min(1, 'SENTRY_DSN is required'),
});
const buildTime = z.object({
// ... other build-time env vars
SENTRY_ORG: z.string().min(1, 'SENTRY_ORG is required'),
SENTRY_PROJECT: z.string().min(1, 'SENTRY_PROJECT is required'),
});
const _clientEnv = {
// ... other client env vars
SENTRY_DSN: process.env.SENTRY_DSN,
};
const _buildTimeEnv = {
// ... other build-time env vars
SENTRY_ORG: process.env.SENTRY_ORG,
SENTRY_PROJECT: process.env.SENTRY_PROJECT,
};
// ... rest of the fileThe SENTRY_AUTH_TOKEN should not be added to the .env file as it's sensitive information that shouldn't be exposed or pushed to version control. Instead, add it as an EAS secret using the Expo dashboard or the EAS CLI:
eas secret:create --scope your-project-name --name SENTRY_AUTH_TOKEN --value your-token-value --type stringThis ensures that your Sentry authentication token remains secure while still being accessible during the build process.
Install the Sentry SDK in your project.
npx expo install @sentry/react-nativeAdd the Sentry plugin config to your app.config.ts file.
// rest of the file
import { ClientEnv, Env } from './env';
export default ({ config }: ConfigContext): ExpoConfig => ({
...config,
// rest of the config
plugins: [
// rest of the plugins
[
'@sentry/react-native/expo',
{
url: 'https://sentry.io/',
organization: Env.SENTRY_ORG,
project: Env.SENTRY_PROJECT,
note: 'Ensure you set the SENTRY_AUTH_TOKEN as an environment variable to authenticate with Sentry. Do not add it to the .env file. Instead, add it as an EAS secret or as an environment variable in your CI/CD pipeline for security.',
// If you are using a self-hosted instance, update the value of the url property
// to point towards your self-hosted instance. For example, https://self-hosted.example.com/.
},
],
],
});Update your metro config to inject a debug ID into your source maps.
/* eslint-env node */
// this replaces `const { getDefaultConfig } = require('expo/metro-config');`
const { getSentryExpoConfig } = require('@sentry/react-native/metro');
const { withUniwindConfig } = require('uniwind/metro');
const config = getSentryExpoConfig(__dirname);
module.exports = withUniwindConfig(config, { cssEntryFile: './global.css' });Initialize Sentry in your app. Create a new file src/lib/sentry.ts and add the following code:
import { Env } from '@env';
import * as Sentry from '@sentry/react-native';
import { useNavigationContainerRef } from 'expo-router';
import { useEffect } from 'react';
const navigationIntegration = Sentry.reactNavigationIntegration({
enableTimeToInitialDisplay: true,
});
export const initSentry = () => {
if (!__DEV__) {
Sentry.init({
dsn: Env.SENTRY_DSN,
integrations: [navigationIntegration],
});
}
};
export const useSentryNavigationConfig = () => {
const navigationRef = useNavigationContainerRef();
useEffect(() => {
if (navigationRef && !__DEV__) {
navigationIntegration.registerNavigationContainer(navigationRef);
}
}, [navigationRef]);
};Then, initialize Sentry and configure it with navigation in your src/app/_layout.tsx file:
import { initSentry, useSentryNavigationConfig } from '@/lib/sentry';
import * as Sentry from '@sentry/react-native';
initSentry();
function RootLayout() {
useSentryNavigationConfig();
return (
<Providers>
<Stack>
<Stack.Screen name="(app)" options={{ headerShown: false }} />
<Stack.Screen name="onboarding" options={{ headerShown: false }} />
...
</Stack>
</Providers>
);
}
// Wrap your app with Sentry
export default Sentry.wrap(RootLayout);This setup will enable Sentry error tracking and performance monitoring in your app.
Add the Apple privacy manifest to prevent any issues with Apple. Create a new file apple-privacy-manifest.json and add the following code:
{
"NSPrivacyCollectedDataTypes": [
{
"NSPrivacyCollectedDataType": "NSPrivacyCollectedDataTypeCrashData",
"NSPrivacyCollectedDataTypeLinked": false,
"NSPrivacyCollectedDataTypeTracking": false,
"NSPrivacyCollectedDataTypePurposes": [
"NSPrivacyCollectedDataTypePurposeAppFunctionality"
]
},
{
"NSPrivacyCollectedDataType": "NSPrivacyCollectedDataTypePerformanceData",
"NSPrivacyCollectedDataTypeLinked": false,
"NSPrivacyCollectedDataTypeTracking": false,
"NSPrivacyCollectedDataTypePurposes": [
"NSPrivacyCollectedDataTypePurposeAppFunctionality"
]
},
{
"NSPrivacyCollectedDataType": "NSPrivacyCollectedDataTypeOtherDiagnosticData",
"NSPrivacyCollectedDataTypeLinked": false,
"NSPrivacyCollectedDataTypeTracking": false,
"NSPrivacyCollectedDataTypePurposes": [
"NSPrivacyCollectedDataTypePurposeAppFunctionality"
]
}
],
"NSPrivacyAccessedAPITypes": [
{
"NSPrivacyAccessedAPIType": "NSPrivacyAccessedAPICategoryUserDefaults",
"NSPrivacyAccessedAPITypeReasons": ["CA92.1"]
},
{
"NSPrivacyAccessedAPIType": "NSPrivacyAccessedAPICategorySystemBootTime",
"NSPrivacyAccessedAPITypeReasons": ["35F9.1"]
},
{
"NSPrivacyAccessedAPIType": "NSPrivacyAccessedAPICategoryFileTimestamp",
"NSPrivacyAccessedAPITypeReasons": ["C617.1"]
}
]
}Then add it to your app.config.ts:
import applePrivacyManifest from './apple-privacy-manifest.json';
export default ({ config }: ConfigContext): ExpoConfig => ({
...config,
// rest of the config
ios: {
// rest of ios config
privacyManifests: applePrivacyManifest,
},
});Read more about Apple Privacy Manifest and Sentry.
Test the Sentry integration. Follow these steps to ensure errors are being reported correctly:
- Run the prebuild command for your project.
- Launch the app in a simulator or on a physical device.
- Use the following code snippet to add error-triggering buttons to your app:
import React from 'react';
import { View, Button } from 'react-native';
import * as Sentry from '@sentry/react-native';
const SentryTestComponent = () => {
const throwJSError = () => {
throw new Error('Test JavaScript Error for Sentry');
};
const triggerNativeError = () => {
Sentry.nativeCrash();
};
return (
<View>
<Button title="Trigger JS Error" onPress={throwJSError} />
<Button title="Trigger Native Error" onPress={triggerNativeError} />
</View>
);
};
export default SentryTestComponent;- Implement this component in your app and interact with the buttons.
- Check your Sentry dashboard to verify that the errors are being reported correctly (wait a moment for the errors to appear).
Remember to remove or disable these test buttons before releasing your app to production.