Start here
Getting started
Install the core module, an OTA adapter, and a sink, wire ReleaseHealth.init, and call markHealthy once your first screen is interactive.
You need three pieces: the core engine, one adapter for your OTA vendor, and at least one sink to export events. The core runs the state machine regardless of which adapter and sink you pick.
Install
For expo-updates (EAS Update), posting events to your own endpoint:
npm install react-native-release-health @release-health/adapter-expo-updates @release-health/sink-httpUsing hot-updater instead? Swap the adapter:
npm install react-native-release-health @release-health/adapter-hot-updater @release-health/sink-httpRequirements
- React Native 0.76 or newer on the New Architecture (bridgeless). The core is a TurboModule; the legacy bridge is not supported.
- iOS and Android. Expo apps work without a config plugin.
- The adapter's vendor SDK as a peer:
expo-updates55+ or@hot-updater/react-native0.35+.
Wire it up
Call init once, as early as possible, then call markHealthy() when your first screen is
genuinely usable.
import { useEffect } from 'react';
import { ReleaseHealth, useReleaseHealth } from 'react-native-release-health';
import { expoUpdatesAdapter } from '@release-health/adapter-expo-updates';
import { httpSink } from '@release-health/sink-http';
ReleaseHealth.init({
adapter: expoUpdatesAdapter(),
sinks: [httpSink({ url: 'https://telemetry.example.com/release-health' })],
healthyTimeoutMs: 15000, // markHealthy() deadline after a fresh update
crashLoopThreshold: 2, // failed launches before rollback is recommended
});
function App() {
// status: 'stable' | 'probation' | 'healthy' | 'suspect' | 'failed' | ...
const { status, activeUpdateId } = useReleaseHealth();
useEffect(() => {
// Call when your first screen is actually usable, not merely rendered.
ReleaseHealth.markHealthy();
}, []);
useEffect(
() =>
ReleaseHealth.onRollbackRecommended(({ updateId, reason }) => {
// Page your on-call, disable the release server-side, or let
// autoRollback handle it where the adapter supports rollback.
}),
[]
);
return <YourApp />;
}The hot-updater adapter wires into HotUpdater.wrap rather than being passed inline; see the
hot-updater adapter for the exact wiring.
Where markHealthy goes
markHealthy() placement is load-bearing. Call it when the first screen is genuinely interactive:
data loaded, navigation responsive. Calling it from a splash screen, or unconditionally at mount,
accepts a broken-but-rendering update and defeats probation. Never calling it on some code path
leaves a healthy update parked at suspect until the next relaunch.
Verify in release builds
__DEV__ short-circuits probation, so nothing is verified in development. To watch the state
machine run, use a release build and a real (or deliberately broken) OTA update. The repo ships
two end-to-end runbooks that do exactly this:
demo.md
for expo-updates and
demo-hot-updater.md
for hot-updater.
Where next
- Core API: every
initoption,markHealthy,onRollbackRecommended,notifyReload, and theuseReleaseHealthhook. - Events and failure detection: the typed event stream and how the crash-loop verdict is reached.
- Limitations: what the heuristic can and cannot detect. Read this before relying on it.