@vonovak/react-native-theme-control
July 6, 2025 · View on GitHub
Version >= 7 supports RN 0.79+ and Expo 53+
Table of Contents
Installation
yarn add @vonovak/react-native-theme-control
OR
npm i @vonovak/react-native-theme-control
Expo
If you want to enable theme persistence across app restarts, or force a light / dark mode, add @vonovak/react-native-theme-control to the plugins entry in your Expo config file.
For example, the following will enable theme persistence across app restarts:
{
"expo": {
"plugins": ["@vonovak/react-native-theme-control"]
}
}
If you want to force light / dark mode always, to resolve issues like this, then specify the theme like this:
{
"expo": {
"plugins": [
[
"@vonovak/react-native-theme-control",
{
"mode": "light"
}
]
]
}
}
Available mode values:
'light'- Forces light mode'dark'- Forces dark mode'userPreference'- Uses system preference (default)
After configuring the plugin, run npx expo prebuild --clean and rebuild your iOS and Android projects.
Native files setup
⚠️ Do not do this if you're using Expo!
There are manual installation steps that need to be performed in vanilla React Native Projects:
Android
Add the following to your MainApplication.kt (or MainApplication.java):
import eu.reactnativetraining.ThemeControlModule;
//...
@Override
public void onCreate() {
super.onCreate();
ThemeControlModule.Companion.recoverApplicationTheme(applicationContext);
SoLoader.init(this, /* native exopackage */ false);
}
Forcing specific themes:
To force dark mode:
ThemeControlModule.Companion.forceTheme(androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES);
To force light mode:
ThemeControlModule.Companion.forceTheme(androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO);
Note: You can optionally set androidxCoreVersion to specify the version of androidx core you want to use.
iOS
Modify the AppDelegate
Recovering the application theme involves modification of native files. The following is required:
- Add to bridging header (usually
ios/YourProjectName-Bridging-Header.h):
#import <RNThemeControl.h>
- Modify AppDelegate.swift in
didFinishLaunchingWithOptions:
#if os(iOS) || os(tvOS)
window = UIWindow(frame: UIScreen.main.bounds)
+ RNThemeControl.recoverApplicationTheme()
factory.startReactNative(
withModuleName: "main",
in: window,
launchOptions: launchOptions)
#endif
Forcing specific themes:
For testing purposes, you can use:
RNThemeControl.forceTheme
However, for production apps, it's recommended to edit the plist file instead.
Usage example
More examples can be found in the example project.
import * as React from 'react';
import { Text, useColorScheme, View } from 'react-native';
import {
setThemePreference,
SystemBars,
ThemePreference,
useThemePreference,
} from '@vonovak/react-native-theme-control';
import SegmentedControl from '@react-native-segmented-control/segmented-control';
export function SimpleScreen() {
const colorScheme = useColorScheme();
const isDarkMode = colorScheme === 'dark';
const themePreference = useThemePreference();
const bgColor = isDarkMode ? '#2A2550' : '#FFF6EA';
const textColor = isDarkMode ? 'white' : 'black';
const barsBackground = isDarkMode ? '#9900F0' : '#A0BCC2';
const dividerColor = textColor;
const textColorStyle = { color: textColor };
const values: Array<ThemePreference> = ['light', 'dark', 'system'];
return (
<View
style={{
backgroundColor: bgColor,
flexGrow: 1,
flexShrink: 1,
alignItems: 'center',
justifyContent: 'space-evenly',
}}
>
<SystemBars
backgroundColor={barsBackground}
dividerColor={dividerColor}
/>
<SegmentedControl
style={{ width: '100%' }}
values={values}
selectedIndex={values.indexOf(themePreference)}
onChange={({ nativeEvent }) => {
setThemePreference(nativeEvent.value as ThemePreference);
}}
/>
<Text style={textColorStyle}>useColorScheme(): {colorScheme}</Text>
<Text style={textColorStyle}>
useThemePreference(): {themePreference}
</Text>
</View>
);
}
Troubleshooting
Android activity restarts upon theme change
Make sure that inside the AndroidManifest.xml file, the android:configChanges include uiMode. For example:
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
Note: Restarting the activity might be necessary for some theme-related changes to occur, for example, for PlatformColor changes to take effect.
Android scroll bar's color is not changing
The list components might need to re-render once the theme changes for the scroll bars to re-draw.
For example, if you're using FlatList, you can add a key prop to it, and change the value of the key prop when the theme changes:
const colorScheme = useColorScheme();
<FlatList key={colorScheme} />;