Expo Go cannot load the SDK’s native code. Use a
development build:
npx expo run:ios, npx expo run:android, or EAS Build. Each one runs
expo prebuild when the android/ and ios/ folders do not exist yet. Prebuild
generates the native projects that the SDK and the config plugins work in.
1. Install the SDK
npx expo install @everframe/react-native
Rebuild the native app afterwards. The SDK needs React Native 0.84 or newer with the New Architecture.
2. Add your SDK key
Mount the provider at the root of your app. With Expo Router, that is the root layout:
// app/_layout.tsx
import { Stack } from 'expo-router';
import { EverframeProvider } from '@everframe/react-native';
export default function RootLayout() {
return (
<EverframeProvider config={{ sdkKey: 'evf_live_…' }}>
<Stack />
</EverframeProvider>
);
}
Without Expo Router, wrap the component your App.tsx exports.
sdkKey is the app’s SDK key (evf_live_…), filled in on the app’s
Setup tab in the dashboard. It is publishable (it can only submit reports),
so it can sit in the bundle or in an EXPO_PUBLIC_ variable.
Crash reporting is on once the provider mounts: uncaught JavaScript errors become reports by themselves, and so do JVM crashes on Android, native crashes, ANRs and low-memory kills on Android 11 and later, and native crashes on iOS. Crash reporting covers what each platform captures. Screens are not tracked until you wire navigation, as described in Install.
3. Add the config plugin
Install the build tooling:
npm i -D @everframe/metro @everframe/expo @everframe/cli
Wrap the Metro config, creating metro.config.js if the project has none:
// metro.config.js
const { getDefaultConfig } = require('expo/metro-config');
const { withEverframe } = require('@everframe/metro');
module.exports = withEverframe(getDefaultConfig(__dirname), { projectRoot: __dirname });
Add the config plugin to app.json, next to any plugins you already have:
{
"expo": {
"plugins": [
[
"@everframe/expo",
{
"appId": "00000000-0000-4000-8000-000000000000"
}
]
]
}
}
Replace the example with the app’s App ID, the UUID on the same Setup
tab. It is not the SDK key: prebuild fails when appId is not a UUID.
Add .everframe/ to .gitignore. Metro writes each build’s identity there.
At prebuild the plugin adds the upload step to android/app/build.gradle and
the Xcode project. It edits the Groovy build.gradle that Expo generates; a
Kotlin-script build.gradle.kts is not supported. If you keep your own
android/ and ios/ folders instead of generating them, see
Expo for the one-time setup command.
4. Build Release with R8 on
Expo’s Android template builds Release without R8, so nothing is shrunk or
obfuscated. To turn it on, install expo-build-properties:
npx expo install expo-build-properties
and add it to the plugins in app.json:
{
"expo": {
"plugins": [
[
"expo-build-properties",
{
"android": {
"enableMinifyInReleaseBuilds": true
}
}
]
]
}
}
The SDK ships its own keep rules, so it keeps working in a minified build. With
R8 on, Java and Kotlin frames in Android crash reports are obfuscated until
Everframe retraces them with that build’s mapping.txt; see
Android R8. With R8 off they arrive readable,
and there is no mapping to upload.
5. Regenerate the native projects
Config plugins change the native projects only when prebuild generates them,
and npx expo run:ios and npx expo run:android prebuild only when android/
and ios/ are missing. Folders generated before you added @everframe/expo or
expo-build-properties keep building without them: no upload step and no R8.
After any change to plugins in app.json, generate them again:
npx expo prebuild --clean
--clean deletes both folders first, so move any edits you made there by hand
into a config plugin before running it. EAS Build runs prebuild by itself when
the folders are not committed.
Test crash reporting with a Release build (npx expo run:android --variant release, npx expo run:ios --configuration Release, or an EAS build). Debug
builds run the JavaScript from Metro, so their stacks do not match what you
ship.
6. Upload symbols
Set EVERFRAME_API_TOKEN, an API token with the artifacts:write scope
(Settings → API tokens), in the environment of your Release builds: your
shell or CI for local builds, and on EAS Build a secret environment variable of
the environment the release profile uses. Never put it in the app or in an
EXPO_PUBLIC_ variable.
Native Release builds then upload what makes their crash frames readable:
- on both platforms, the Hermes bytecode and composed source map for JavaScript frames;
- on iOS, the app’s dSYMs for native frames;
- on Android, the R8 mapping (when R8 is on) and the app’s native libraries,
through the
dev.everframeGradle plugin that the config plugin applies.
Expo has the details. Without the token the upload is skipped, the build still succeeds, and that build’s frames stay raw.
An over-the-air update (eas update) ships a new bundle without a native
build. Upload its maps with npx @everframe/cli upload-expo-export, as described in
Expo.
Which value goes where
| Where it goes | Value |
|---|---|
EverframeProvider config.sdkKey | The SDK key, evf_live_… |
@everframe/expo appId, or EVERFRAME_APP_ID | The App ID, a UUID |
EVERFRAME_API_TOKEN in the build environment | An API token with the artifacts:write scope |
Requirements
| Expo | A development build or EAS Build. Expo Go is not supported. |
| React Native | 0.84+, New Architecture. Hermes supported. |
| Android build file | The Groovy android/app/build.gradle that prebuild generates |
| Node | 22+ for @everframe/metro, @everframe/expo and @everframe/cli |
| TV | react-native-tvos with @react-native-tvos/config-tv. TVs have no on-device reporter; see Install. |
Verify it
In a Release build, throw from a button:
<Button title="Crash test" onPress={() => { throw new Error('Everframe crash test'); }} />
React Native closes a Release app on an uncaught error, so the report is sent
when the app starts again. It then appears under Errors in the dashboard.
If its frames are raw, check that the build had EVERFRAME_API_TOKEN and that
the build log shows the upload.
Next
- Crash reporting — what is caught, and promise rejections
- Configuration — every option
- Source maps & R8 mappings — everything the upload step does