React Native module to detect whether the current run-time is Simulator, TestFlight-like, Production, or Unknown.
npm install react-native-release-trackDetect at runtime whether your React-Native app is running in:
* SIMULATOR – iOS Simulator / Android Emulator
TESTFLIGHT – TestFlight build on iOS, or* any non-Play-Store / pre-release build on Android
* PRODUCTION – Production release from the App Store / Play Store
* UNKNOWN – Indeterminate state when detection isn't yet possible (very early app init)
The library exposes a constant and a function so you can branch logic or surface UI messages without shipping separate builds.
---
``bash`with npm
yarn add react-native-release-trackor
yarn add react-native-release-trackif you use Expo managed workflow
npx expo install react-native-release-track
This is an Expo Modules–style package. If you are on bare React-Native the native code is automatically linked via pod install (iOS) and Gradle (Android).
---
`ts
import ReleaseTrack, { Environment } from 'react-native-release-track';
// 1. constant (preferred – synchronous)
console.log(ReleaseTrack.environment); // Environment.SIMULATOR | Environment.TESTFLIGHT | Environment.PRODUCTION | Environment.UNKNOWN
// 2. function (identical value, but callable later)
const env: Environment = ReleaseTrack.getEnvironment();
if (env === Environment.TESTFLIGHT) {
// ...
}
`
When bundling with react-native-web the module resolves to null. Guard your calls accordingly:
`ts`
if (ReleaseTrack && ReleaseTrack.environment === 'TESTFLIGHT') {
// …
}
---
| Situation | Value returned |
|------------------|----------------|
| Simulator | SIMULATOR |TESTFLIGHT
| TestFlight (sandbox receipt) | |PRODUCTION
| App Store build | |UNKNOWN
| Early init (context unavailable) | |
Logic lives in detectEnvironment() inside the Swift module and relies on:
* #if targetEnvironment(simulator)Bundle.main.appStoreReceiptURL?.lastPathComponent == "sandboxReceipt"
*
No extra configuration required.
---
Android doesn't expose which Play track (internal, alpha, beta) the app came from. We therefore use a pragmatic hierarchy:
1. Override via manifest – you can explicitly mark a build as test by adding:
`xml`
android:value="test"/>
You can inject this only in your internal, alpha, or beta productFlavors so production stays clean.
2. Emulator detection – Build fingerprint contains generic/emulator → SIMULATOR.com.android.vending
3. Installer package – If the app wasn't installed from the official Play Store () it's considered TESTFLIGHT (covers sideloads, alternative stores, etc.).UNKNOWN
4. Fallback → (very early init) otherwise PRODUCTION.
> Note: Because Google keeps track membership on its servers, Play Store internal/alpha/beta installs cannot be distinguished from production at runtime – you must use the manifest override (or another build-time flag) if you need that distinction.
---
| Member | Type | Platform | Description |
|-----------------------|-----------|----------|-------------|
| environment | Environment | native | Synchronous constant. Runtime environment enum. May be UNKNOWN very early. |getEnvironment()
| | () => Environment | native | Same value, callable any time. |
On Web the default export is null and therefore none of the members exist.
---
A minimal Expo example is in the example/ folder. It prints the detected environment:
`tsx
import ReleaseTrack, { Environment } from 'react-native-release-track';
import { Text } from 'react-native';
export default function App() {
return
}
`
---
Found a bug or have an improvement? Please open an issue or PR.
1. Clone repo & install deps: pnpm install / yarn.expo run:ios
2. Run the example app via or run:android.main`.
3. Submit PR targeting
---
MIT © 2025 Jason Safaiyeh