expo-app-lifecycle-plus
v0.1.0
Published
this module handle manage app states
Maintainers
Readme
About The Project
expo-app-lifecycle-plus exposes a single event channel and typed payloads so you can observe app state transitions consistently from JavaScript.
It is designed for real-world lifecycle analytics and debugging:
addListener((event) => ...)to stream lifecycle eventsgetCurrentState()to read current native state snapshot- iOS launch/scene signals and inferred termination support
- Android process foreground/background and optional activity focus/blur
Built With
- Expo Modules
- React Native
- AndroidX Lifecycle
- UIKit UIApplication Notifications
- UIKit UIScene Notifications
Getting Started
Prerequisites
- Node.js LTS (
20or22recommended) - Expo / React Native app
Installation
Expo managed or prebuild:
npx expo install expo-app-lifecycle-plusBare React Native / Expo bare:
npm install expo-app-lifecycle-plus
# or
bun add expo-app-lifecycle-plusRebuild native apps after install:
npx expo run:android
npx expo run:iosUsage
import * as Lifecycle from 'expo-app-lifecycle-plus';
const current = Lifecycle.getCurrentState();
console.log('current state', current);
const sub = Lifecycle.addListener((event) => {
console.log('lifecycle event', event);
});
// later
sub.remove();Example Event Payload
{
"type": "foreground",
"state": "foreground",
"timestamp": 1700000000000,
"platform": "ios"
}Event Reference
Common Events
jsReloadcoldStartforegroundbackground
iOS Events
appLaunchactiveinactivesceneActivesceneInactivewillTerminate(best-effort; not guaranteed)inferredTermination(emitted on next launch if app was previously backgrounded and appears to have been killed)
Android Events
focusActivityblurActivity
Event Type
type LifecycleEvent = {
type:
| 'jsReload'
| 'coldStart'
| 'appLaunch'
| 'inferredTermination'
| 'foreground'
| 'background'
| 'active'
| 'inactive'
| 'willTerminate'
| 'sceneActive'
| 'sceneInactive'
| 'focusActivity'
| 'blurActivity';
state: 'unknown' | 'foreground' | 'background' | 'active' | 'inactive';
timestamp: number;
platform: 'ios' | 'android';
activity?: string;
source?: 'didFinishLaunching' | 'observerStart';
inferredFrom?: 'previousBackground';
previousBackgroundTimestamp?: number;
elapsedSinceBackgroundMs?: number;
};API Reference
addListener(listener): EventSubscription
Subscribes to native lifecycle updates on onLifecycleEvent.
addListener(listener: (event: LifecycleEvent) => void): EventSubscriptiongetCurrentState(): LifecycleState
Returns current native lifecycle state snapshot.
getCurrentState(): 'unknown' | 'foreground' | 'background' | 'active' | 'inactive'Platform Notes
willTerminateis not guaranteed on mobile OSes.- On iOS, app kills in background may not emit a terminate callback. Use
inferredTerminationfor practical analytics. - On Android, process lifecycle (
foreground/background) is generally the most reliable signal. jsReloadcan happen during development due to fast refresh/reload and does not always mean a fresh process launch.
Troubleshooting
I only see inactive -> background -> foreground -> active
This is normal lifecycle flow on iOS when app moves between foreground/background.
willTerminate is not emitted
Expected on many real-device scenarios. Mobile OS may kill apps without firing terminate callbacks.
coldStart appears more than once in development
Development reload / fast refresh can recreate JS/runtime state. Use jsReload and appLaunch together to interpret startup behavior.
TypeScript import errors
Rebuild package and restart Metro:
npm run buildContributing
PRs and issues are welcome.
- Fork the project
- Create branch
- Commit changes
- Push branch
- Open PR
License
MIT © Adem Hatay
