rn-app-restart
v1.0.1
Published
Restart your React Native app natively. True cold restart on Android, dev-menu-grade reload on iOS with a launch-screen overlay. Supports both the New Architecture (TurboModule) and the old architecture.
Maintainers
Readme
rn-app-restart
Restart your React Native app natively.
A small, dependency-free native module that restarts your React Native app. It is a drop-in replacement for react-native-restart and ships a real codegen TurboModule on the New Architecture.
On Android you get a true cold restart. The launch intent is relaunched with a cleared task and the process exits, so the app starts in a fresh process rather than reusing a torn-down JS context. React Native's own RTL direction is committed to disk before the process is killed, so I18nManager.forceRTL reliably applies on the very next launch instead of needing a second restart.
On iOS, Apple does not allow an app to relaunch its own process. So restart() triggers React Native's reload-command listeners, the same mechanism the dev-menu reload uses, and covers the transition with your app's own LaunchScreen.storyboard. You see your launch screen instead of a flash of white.
The usual reason to need any of this is an RTL/LTR language switch, which React Native only applies after a restart.
Requirements
- React Native 0.74 or newer (it needs
BaseReactPackage). Verified end to end on RN 0.86 with the New Architecture and bridgeless, on both iOS and Android. Both architectures compile on RN 0.79 and 0.86. - iOS 13+, Android minSdk 24+. If your app sets higher values, the module follows them.
- Both architectures work with nothing to configure. The module follows whatever the host app uses.
- Expo dev client, prebuild, EAS, and bare all work. Expo Go does not, for reasons covered in Expo.
Installation
- Install the package:
yarn add rn-app-restart
# or
npm install rn-app-restart- On iOS, install the pods:
cd ios && pod install- Rebuild both apps. This is a native module, so a Metro or JS reload will not pick it up. Run
yarn iosandyarn android, or build from Xcode and Android Studio. On Expo, runnpx expo prebuildif you use CNG, thennpx expo run:iosornpx expo run:android, or make an EAS build.
Autolinking handles registration on both platforms. There is nothing to add to MainApplication, no config plugin, and no manifest changes.
Troubleshooting
| Symptom | Cause and fix |
| --- | --- |
| [rn-app-restart] Native module not found... | The installed binary predates the package. Rerun pod install and rebuild both apps. If you are in Expo Go, custom native modules cannot run there at all, so use a dev build. In development the call falls back to a JS reload so you can keep working. |
| Jest tests crash on import | Add the one-line mock. See Testing. |
| iOS: launch screen overlay stays about 8 seconds before fading | Neither bundle-loaded signal fired on your RN version, so the failsafe dismissed it. You are never trapped, but expected dismissal is about 0.5 seconds. Please open an issue with your RN version and architecture. |
| iOS: no overlay at all during restart | Your app has no UILaunchStoryboardName in Info.plist, so there is no launch storyboard to show. The reload still works, it just is not covered. Add a launch screen if you want the covered transition. |
| Android: app closes but does not reopen | The launcher could not resolve your launch intent. Check that MainActivity has the MAIN and LAUNCHER intent filter, which every RN template includes by default. |
Usage
import { restart } from 'rn-app-restart';
restart();If you are migrating from react-native-restart, the default export is API-compatible. Change the import and you are done:
import RNRestart from 'rn-app-restart';
RNRestart.restart(); // or RNRestart.Restart()Applying an RTL language switch
import { I18nManager } from 'react-native';
import { restart } from 'rn-app-restart';
export const setLanguage = async (lang: 'ar' | 'en') => {
await persistLang(lang); // your storage, awaited before the restart
const rtl = lang === 'ar';
if (I18nManager.isRTL !== rtl) {
I18nManager.allowRTL(rtl);
I18nManager.forceRTL(rtl);
restart(); // direction only applies after a restart
}
};Await anything you need persisted before calling restart(). On Android the restart kills the process, so an in-flight AsyncStorage write can be lost partway through. The module commits React Native's own RTL preferences synchronously before exiting, but it has no way to flush your app's storage for you. If your storage is synchronous, like MMKV, this is not a concern.
The restart "loading screen"
There is nothing to configure. Each app's own launch experience becomes the loading screen.
| Platform | What the user sees during restart |
| --- | --- |
| Android | The process relaunches through your normal startup, so you get your launch theme or splash. This works well with react-native-bootsplash. |
| iOS | Your LaunchScreen.storyboard appears as an overlay window the moment restart() is called, then fades out about 0.3 seconds after the fresh JS bundle loads. A hard 8 second timeout means a failed load can never trap the user. If the app has no launch storyboard, the reload happens without an overlay. |
To change how a restart looks in a given project, change that project's launch screen. The module always mirrors it.
Expo
| Environment | Supported | Notes |
| --- | --- | --- |
| Dev client, expo prebuild (CNG) | Yes | Works out of the box. No config plugin is needed because there are no manifest or Info.plist changes. |
| EAS builds | Yes | Same. Install and build. |
| Bare Expo | Yes | Standard autolinking. |
| Expo Go | No | Expo Go cannot run custom native modules at all, for any package. In development, restart() falls back to a JS reload with a warning so you can keep working. In production builds without the native module it throws with setup instructions. Use a development build. |
Testing (Jest)
The package ships a mock. Add one line to your Jest setup:
jest.mock('rn-app-restart', () => require('rn-app-restart/jest/mock'));Architecture support
| | Old architecture (Paper) | New Architecture (Fabric, bridgeless) |
| --- | --- | --- |
| JS | Same import. TurboModuleRegistry falls back to the classic registry | Codegen'd TurboModule spec |
| Android | Classic ReactContextBaseJavaModule | Codegen spec (NativeRNAppRestartSpec) |
| iOS | RCT_EXPORT_MODULE and RCT_EXPORT_METHOD | Same class plus getTurboModule under RCT_NEW_ARCH_ENABLED |
There are no newArchEnabled flags to set. The module follows whatever the host app uses. Runtime-verified on RN 0.86 with the New Architecture and bridgeless on both platforms. The old-architecture paths compile on RN 0.79 and 0.86.
Why not react-native-restart?
It ships a real codegen TurboModule on the New Architecture rather than going through the legacy interop layer. On Android it does a true cold restart in a fresh process instead of a JS-context reload, which is what makes native flags like RTL direction apply cleanly. On iOS it covers the reload with your launch screen rather than showing the teardown. It has no dependencies and about 200 lines of native code.
How it works
On Android: packageManager.getLaunchIntentForPackage() with FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK, then startActivity, finishAffinity(), and a process exit. The OS relaunches the app fresh.
On iOS: RCTTriggerReloadCommandListeners(), the supported public reload entry point, tears down and recreates the React host and all its surfaces. The launch-screen overlay window sits at UIWindowLevelAlert + 1 and dismisses on whichever bundle-loaded signal the host runtime posts: RCTJavaScriptDidLoadNotification on the bridge, or RCTInstanceDidLoadBundle on bridgeless. An 8 second timeout is the fallback. Both signals are needed, because the bridge notification is not posted in bridgeless mode, which is the default on modern React Native.
