Implementing Dark Mode in React Native
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;
Mega Bundle Sale is ON! Get ALL of our React Native codebases at 90% OFF discount π₯
Get the Mega BundleDefining 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 ~ 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};
`;
Mega Bundle Sale is ON! Get ALL of our React Native codebases at 90% OFF discount π₯
Get the Mega BundleLastly, 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.
Looking for a custom mobile application?
Our team of expert mobile developers can help you build a custom mobile app that meets your specific needs.
Get in TouchConclusionβ
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
-
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 manyReact Native template.