In this tutorial, we are taking a look at how to detect and support dark mode in React Native apps. You are going to build a small demo app that sets its appearance based on the platform OS. The platform OS will have two theme modes, dark or light. By default, when the app will start, it is going to have the theme based on the platform OS but the user is going have an option to toggle between the themes.
Configure react-native-appearance
To start, let us create a new React Native project by executing the following command and install the required dependencies to build this app.
# create a new project react-native init rnDarkModeStyledComponentsDemo # navigate inside the project directory cd rnDarkModeStyledComponentsDemo # install the following dependencies yarn add styled-components react-native-appearance
iOS developers have to install pods to complete the configuration for react-native-appearance
 by running the following commands:
cd ios/ pod install
Android developers have to add the following configuration. First, open the file android/app/src/main/AndroidManifest.xml
 and add the uiMode
 flag.
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode">
Next, open android/app/src/main/java/com/rnDarkModeStyledComponentsDemo/MainActivity.java
 and add the following.
import android.content.Intent; import android.content.res.Configuration; // inside public class MainActivity extends ReactActivity @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); Intent intent = new Intent("onConfigurationChanged"); intent.putExtra("newConfig", newConfig); sendBroadcast(intent); }
Define Themes
In this section, let us define two basic themes in two separate files. Create a new directory called src/themes/
 and inside it create two new files: light.js
 and dark.js
. Inside light.js
, let us define a basic set of colors to be used when for different attributes when this theme is active. Add the following snippet and do not forget to export it.
const light = { theme: { background: '#ededed', border: '#bdbdbd', backgroundAlt: '#eaeaeb', borderAlt: '#bdbdbd', text: '#171717' } } export default light
Next, open dark.js
 file and similarly, add the theme values for it.
const dark = { theme: { background: '#2E3440', border: '#575c66', backgroundAlt: '#575c66', borderAlt: '#2E3440', text: '#ECEFF4' } } export default dark
Defining a Theme Manager using context
In this section, let us define a theme context manager that will switch or toggle between the two themes based on the value of the theme provided. The user is going to have access to a switch button that they can use to toggle between the two themes. Apart from that, using the module react-native-appearance
, this react native app is going to access the operating system’s appearance too. To start, import the following statements. Using styled-components
 has the benefit that it supports theming to the full extent by providing <ThemeProvider>
 to wrap the context component.
import React, { createContext, useState, useEffect } from 'react' import { StatusBar } from 'react-native' import { ThemeProvider } from 'styled-components/native' import { Appearance, AppearanceProvider } from 'react-native-appearance' import lightTheme from './light' import darkTheme from './dark'
Next, create a ThemeContext
 that is going to hold the value of the current theme (or mode) and a helper function to change that value.
const ThemeContext = createContext({ mode: defaultMode, setMode: mode => console.log(mode) })
Then, useTheme
 is going to be a helper function that uses the previously created ThemeContext
. Do not forget to export it, since you will be using it directly in the UI component later.
export const useTheme = () => React.useContext(ThemeContext)
Let us define a Theme Manager Provider that is going to take care of setting the theme, changing the state or mode of the current theme to the next and using useEffect
 hook, it is going to listen to the theme changes made by the operating system. This listening is done by adding a subscription using addChangeListener
 from react-native-appearance
. Lastly, wrap children of the component inside the ThemeProvider
 imported from styled-components/native
. The children here are going to be the StatusBar
 component from react-native
 as well as the other UI components passed as children
 itself.
const ManageThemeProvider = ({ children }) => { const [themeState, setThemeState] = useState(defaultMode) const setMode = mode => { setThemeState(mode) } useEffect(() => { const subscription = Appearance.addChangeListener(({ colorScheme }) => { setThemeState(colorScheme) }) return () => subscription.remove() }, []) return ( <ThemeContext.Provider value={{ mode: themeState, setMode }}> <ThemeProvider theme={themeState === 'dark' ? darkTheme.theme : lightTheme.theme}> <> <StatusBar barStyle={themeState === 'dark' ? 'light-content' : 'dark-content'} /> {children} </> </ThemeProvider> </ThemeContext.Provider> ) }
Lastly, the root of the app has to be wrapped inside the AppearanceProvider
 to make the OS changes work and listen to OS subscriptions.
const ThemeManager = ({ children }) => ( <AppearanceProvider> <ManageThemeProvider>{children}</ManageThemeProvider> </AppearanceProvider> ) export default ThemeManager
Using ThemeManager inside the App
To use the ThemeManager
 to listen to theme mode changes or at the same time, allow the user to make changes to the appearance of the app, manually by toggling, start by importing the following statements inside App.js
. To let the user toggle between two themes, let us the Switch
 component from react-native
.
import React from 'react' import styled from 'styled-components/native' import ThemeManager, { useTheme } from './src/themes/ThemeContext' import { Switch } from 'react-native'
Now create a HomeScreen
 component that is going to have the Switch
 component wrapped inside Container
 created using styled-components. Using props
 from styled-components
, the background of the theme can be easily switched.
const HomeScreen = () => { const theme = useTheme() return ( <Container> <Switch value={theme.mode === 'dark'} onValueChange={value => theme.setMode(value ? 'dark' : 'light')} /> </Container> ) } const Container = styled.View` flex: 1; justify-content: center; align-items: center; background: ${props => props.theme.background}; `
Lastly, wrap the HomeScreen
 component inside ThemeManager
 to make it work.
const App = () => ( <ThemeManager> <HomeScreen /> </ThemeManager> ) export default App
Testing Dark Mode in React Native
I am going to test this app inside an iOS simulator. By default, the iOS simulator I am running has a light theme or mode. To find where you can switch between appearances on an iOS simulator, open Settings and then you are going to come across a Developer menu as shown below. Open that, and you are going to come across the first thing Appearance. By default, you can see it is set to light mode. Go back to the terminal window, build the app by using the command react-native run-ios
, if you haven’t and when the app opens for the first time, it will show the lighter background and StatusBar
 component is dark in color. Here is the complete demo when OS appearance setting changes, it directly reflects in our React Native app. Â
Conclusion
As you can see, adding dark mode support in React Native apps is pretty straightforward when using react-native-appearance npm package. The nice thing is that this works for both iOS and Android devices that support dark mode.
Next Steps
Now that you have learned about resources to learn React Native development, here are some other topics you can look into
- Firebase —  Push notifications | Firebase storage
- How To in React Native — WebView | Gradient| Camera| Adding GIF| Google Maps | Redux | Debugging | Hooks| Dark mode | Deep-link | GraphQL | AsyncStorage | Offline |Chart | Walkthrough | Geolocation | Tinder swipe | App icon | REST APIÂ
- Payments — Apple Pay | Stripe
- Authentication — Google Login| Facebook login | Phone Auth |Â
- Best Resource – App idea | Podcast | Newsletter| App template
If you need a base to start your next React Native app, you can make your next awesome app using many React Native template.