Prevent automatic screen sleep while your React Native app needs an active screen.
@marcocrupi/react-native-keep-awake-plus supports Android and iOS, with hooks,
a component and independent task handles backed by shared ownership.
This guide describes 2.0.1. Upgrading from 1.x? Read the migration guide for the shared-ownership behavior and breaking changes. Maintainers track publication separately in the release checklist.
- Keep awake while mounted, focused or enabled by app state.
- Overlapping screens and tasks hold independent requests.
- Idempotent task cleanup and imperative calls.
- TypeScript declarations, React Native autolinking and New Architecture Codegen.
Installation · Quick start · API reference · Ownership · React Navigation · Troubleshooting · Maintainer guides
Install 2.0.1 with either package manager:
npm install @marcocrupi/react-native-keep-awake-plus@2.0.1yarn add @marcocrupi/react-native-keep-awake-plus@2.0.1The commands pin the version covered by this guide. To work from a local checkout, follow the example guide.
React Native autolinks the native module. On Android, rebuild the app; no
manual package registration or extra manifest permission is needed in a standard
autolinked project. Use the SDK, NDK, Java and Gradle versions required by your RN
template. The library reads the consumer's compileSdkVersion and minSdkVersion
from rootProject.ext; it does not set the application's target SDK.
On iOS, install Pods and rebuild the native app:
cd ios
bundle exec pod install
cd ..Use pod install if your project does not manage CocoaPods with Bundler. The app's
React Native CocoaPods setup must provide min_ios_version_supported and
install_modules_dependencies. The podspec inherits that RN minimum; it does not
promise support for every iOS release. Codegen runs in the consumer build;
generated sources are not shipped.
Keep the New Architecture enabled (it is the default from RN 0.76 and the
only option from RN 0.82). Legacy bridge sources remain for older RN versions
that allow Old Architecture, with historical coverage only. RN 0.87+ / AGP 9
uses the official template settings
android.newDsl=false and android.builtInKotlin=false; true/true is
not qualified.
Expo: Expo Go does not contain this native module. Use a native development build and regenerate/rebuild the native app after installation. This repository does not qualify a specific Expo SDK.
Replacement package: remove @sayem314/react-native-keep-awake before using
this fork. Native module, pod, Codegen and Android package identifiers retain the
original names. Installing both packages can cause native collisions.
Supported: every stable React Native release with the New Architecture, from 0.76 onward, on Android and iOS.
| Scope | Versions and evidence |
|---|---|
| npm peers | React >=18.2.0; RN >=0.73.0, with no upper bound |
| Native CI on every PR | RN 0.88.0, 0.87.1, Android/iOS New Architecture |
| Weekly support-window CI | RN 0.82.1, 0.78.3, 0.76.9 plus the PR lanes |
| Regression anchor | RN 0.85.3, included example and separate manual CI selection |
| Best effort | RN 0.73–0.75 and Old Architecture; historical RN 0.73.11 checks |
New RN releases install without a peer conflict. A weekly monitor flags each new release, and its Android/iOS lanes are added to the matrix. Until then the release is supported but not yet CI-verified; please report any regression. The window CI covers the 0.76 floor, the React 19 switch (0.78) and the removal of the Old Architecture (0.82). Root RN 0.86.0 is development tooling, not a matrix lane. See native compatibility for the policy, toolchains and build, host-test and runtime boundaries.
Keep the screen awake while this screen is mounted:
import React from 'react';
import {Text, View} from 'react-native';
import {useKeepAwake} from '@marcocrupi/react-native-keep-awake-plus';
export default function ReadingScreen() {
useKeepAwake();
return <View><Text>The screen stays awake while reading.</Text></View>;
}Or mount the default component in JavaScript or TSX:
import React from 'react';
import {Text, View} from 'react-native';
import KeepAwake from '@marcocrupi/react-native-keep-awake-plus';
export default function ReadingScreen() {
return <View><KeepAwake /><Text>Reading</Text></View>;
}Unmounting releases that component's request. Other active owners remain active.
All functions are synchronous from JavaScript's perspective. Their return values acknowledge JavaScript ownership, not the operating system's observed flag.
| API | Parameters | Returns | Behavior / use case |
|---|---|---|---|
useKeepAwake() |
None | void |
Owns a request from effect setup until cleanup |
useKeepAwake({enabled}) |
Optional options; enabled?: boolean, default true |
void |
Owns a request only while mounted and enabled |
<KeepAwake /> |
None | Renders null |
Declarative owner for its mounted lifetime |
<KeepAwake enabled={value} /> |
enabled?: boolean, default true |
Renders null |
Conditional declarative owner |
acquireKeepAwake() |
None | () => void |
Acquires an independent task owner; returned release is idempotent |
activateKeepAwake() |
None | void |
Acquires the single implicit root request, idempotently |
deactivateKeepAwake() |
None | void |
Releases only the implicit root request; absent request is a no-op |
Legacy <LegacyKeepAwake /> |
No keep-awake options | Renders null |
Each mounted Class instance owns an independent request |
LegacyKeepAwake.activate() |
None | void |
Acquires the separate implicit Class request, idempotently |
LegacyKeepAwake.deactivate() |
None | void |
Releases only the implicit Class request, idempotently |
Only the component is a default root export. The hook and functions are named
exports. KeepAwakeOptions and KeepAwakeProps are TypeScript types with an
optional boolean enabled. Legacy Class is a default deep import from /Class
or /Class.js; both paths resolve to the same Class family.
Call hooks on every render; change enabled instead of calling a hook conditionally.
This complete example keeps the screen awake during a timed workout:
import React, {useEffect, useState} from 'react';
import {Button, Text, View} from 'react-native';
import {useKeepAwake} from '@marcocrupi/react-native-keep-awake-plus';
export default function WorkoutScreen() {
const [remaining, setRemaining] = useState(0);
const running = remaining > 0;
useKeepAwake({enabled: running});
useEffect(() => {
if (!running) return;
const timer = setInterval(() => setRemaining(n => Math.max(0, n - 1)), 1000);
return () => clearInterval(timer);
}, [running]);
return (
<View>
<Text>{remaining} seconds remaining</Text>
<Button title="Start 60 seconds" onPress={() => setRemaining(60)} />
<Button title="Stop" onPress={() => setRemaining(0)} />
</View>
);
}The equivalent component expression is <KeepAwake enabled={running} />, with
KeepAwake imported as the default export. Omitting enabled means true.
Switching to false or unmounting releases the owner; switching back acquires a new
one. Recreating the options object alone does not reacquire. StrictMode effect
setup/cleanup replay balances ownership. This example is a foreground UI timer;
keep-awake does not make timers accurate or runnable in the background.
Use one acquisition per operation, including overlapping operations. This helper
accepts your operation and returns its result; cancellation must settle the
operation so finally runs:
import {acquireKeepAwake} from '@marcocrupi/react-native-keep-awake-plus';
export async function withScreenAwake<T>(operation: () => Promise<T>): Promise<T> {
const release = acquireKeepAwake();
try {
return await operation();
} finally {
release();
}
}Acquire from an effect or event/task handler, never during render. Always clean up
on completion, failure and cancellation. A release error propagates from finally
and can replace an operation error; applications needing both errors should
capture/report them separately. The token does not cancel the operation.
This JavaScript example needs no React component. Run it with no existing owners to observe the first/last-owner transitions described in the comments:
import {
acquireKeepAwake,
activateKeepAwake,
deactivateKeepAwake,
} from '@marcocrupi/react-native-keep-awake-plus';
export function demonstrateOwners() {
const releaseWorkout = acquireKeepAwake(); // first owner: native activate
try {
const releaseUpload = acquireKeepAwake();
try {
activateKeepAwake();
activateKeepAwake(); // one implicit root request, not two
deactivateKeepAwake(); // both task owners remain
deactivateKeepAwake(); // no-op
} finally {
try {
deactivateKeepAwake(); // safe even if activation failed
} finally {
releaseUpload(); // workout remains
releaseUpload(); // no-op
}
}
} finally {
releaseWorkout(); // last owner: native deactivate
}
}The implicit root request suits a single application-controlled switch. Use tokens when independent callers need independent cleanup. Pair root activation with root deactivation; do not mix it with Class static methods.
import React from 'react';
import LegacyKeepAwake from '@marcocrupi/react-native-keep-awake-plus/Class';
export default function LegacyScreen() {
return <LegacyKeepAwake />;
}
export function startLegacyRequest(): void {
LegacyKeepAwake.activate();
}
export function stopLegacyRequest(): void {
LegacyKeepAwake.deactivate();
}Each instance acquires in componentDidMount and releases in
componentWillUnmount. Static calls hold a separate Class request and do not
release mounted instances or root requests. There is no Class enabled option;
prefer the root hook/component for new conditional code.
All native public APIs share KeepAwakeOwners.js within a loaded package
instance in one JavaScript runtime. Every hook, component, mounted Class instance
and token owns an independent request. Root imperative functions hold one implicit
request; Class static methods hold another. Repeated implicit activation does not
increment the count. Release is idempotent and can never release another owner.
The first owner requests native activation; only the last release requests native deactivation. Intermediate owners do not issue redundant native calls. There is no public force-off operation. Native coordinators combine requests from valid native module instances/runtimes; the JS coordinator itself does not span runtimes or independently installed package copies.
Android reconciles FLAG_KEEP_SCREEN_ON on Activity/window lifecycle changes.
iOS reconciles UIApplication.idleTimerDisabled on the main queue. Native
invalidation revokes that runtime's ownership and fences stale callbacks; handles
do not transfer to a new runtime. JS cleanup does not synchronously confirm an OS
write. There is no public asynchronous OS-state getter. Other software writing
the same flags can interfere; absolute isolation from external writers is not
promised.
Native activation errors propagate without retaining an owner, so a later acquisition can retry. If final native deactivation throws, the release and its implicit slot are already consumed. Repeating release does not retry the failed native operation or affect a later owner. Report failures in your task/event handling. Hook and Class lifecycle errors follow React's effect/lifecycle error handling. Successful JS calls express intent, not measured screen state.
A blurred screen may stay mounted. If your app already uses React Navigation, use its focus boolean to own a request only while focused. Render this screen inside your existing navigator:
import React from 'react';
import {Text} from 'react-native';
import {useIsFocused} from '@react-navigation/native';
import {useKeepAwake} from '@marcocrupi/react-native-keep-awake-plus';
export default function FocusedScreen() {
const isFocused = useIsFocused();
useKeepAwake({enabled: isFocused});
return <Text>Keep awake only while this screen is focused.</Text>;
}React Navigation is optional and is not a dependency of this package. Other
navigation systems can pass their focus boolean as enabled.
2.0.0 is a major release: imperative deactivate no longer overrides other owners. Simple matching root pairs and existing declarative ownership usually need no rewrite. Applications using deactivate as a global off switch or mixing root and Class families must change. See the 1.6.1 → 2.0.0 guide for before/after examples and API selection.
| Symptom | Check |
|---|---|
| Native module unavailable | Rebuild the native app after install; install iOS Pods; verify autolinking and that you are not using Expo Go |
| Screen remains awake after deactivate | Release tokens, disable/unmount declarative owners, and release the matching root or Class request; there is no global off switch |
| Screen stays awake after navigation | Mounting does not track focus; pass focus as enabled |
| Native collisions / duplicate symbols | Remove the original keep-awake package and rebuild with only one implementation |
| Android SDK policy error | Ensure the app's rootProject.ext defines compileSdkVersion and minSdkVersion as its RN template expects |
| AGP 9 Kotlin extension error with true/true | Restore the official RN 0.87+ false/false settings; see compatibility notes |
RN 0.76–0.78 iOS: consteval error in fmt |
Upstream RN/Xcode issue, not this package: build with Xcode 16.x or upgrade React Native |
| iOS helper or generated header missing | Check RN's CocoaPods setup, rerun Pods/Codegen and rebuild; do not hand-copy generated files |
| No wake lock in a browser/test environment | The non-native root entry is intentionally a no-op; it does not implement the browser Wake Lock API |
Native entrypoints are import-safe if the module is missing: acquisition that
needs native activation throws ReactNativeKCKeepAwakeUnavailableError with a
rebuild/autolinking message. Deactivate without an implicit request is a no-op.
The non-native root entry accepts the same options and returns an idempotent
no-op token release. Legacy Class deep imports are native entrypoints.
On 2026-10-15, the 2.0.0 implementation commit passed JavaScript CI and native compatibility CI. JS CI covers types, packaging, example lint and Jest. Native CI covers fresh archive consumers, Codegen, Android host lifecycle tests and Debug/Release builds, and iOS simulator compilation. Those pipelines do not run device runtime tests.
For 2.0.1, local archive-consumer lanes passed on Android for RN 0.76.9, 0.78.3, 0.82.1, 0.87.1 and 0.88.0. On iOS (Xcode 27) they passed for 0.82.1 and 0.88.0. RN 0.76/0.78 iOS run on Xcode 16.4 in CI; see the 2.0.1 lane evidence.
Separate local RN 0.87.1 checks exercised mixed ownership and actual OS flags on an Android emulator and iOS simulator. They do not qualify physical devices, store signing, every OS version or same-process iOS scene reconnection.
Maintainer guides:
- Example development and manual smoke checks
- Native matrix, reproduction and evidence limits
- Release readiness and publication checklist
- Changelog
This prevents automatic screen sleep; it does not unlock a device, hold a CPU wake lock, guarantee background execution or override system policy. Keeping the screen on consumes power; release requests when no longer needed.
The package ships no runtime npm dependencies. Development tooling has a known
residual braces advisory;
security remediation is not a claim of a vulnerability-free repository. See the
release checklist for audit status, and audit your own resolved consumer graph.
An independently maintained fork of @sayem314/react-native-keep-awake, maintained
by Marco Crupi, based on work by Kyle Corbitt and Sayem Chowdhury. This fork is not
affiliated with or endorsed by the original maintainers unless they state otherwise.
Original license and copyright notices are preserved. Licensed under MIT.