GSD
5 Easy Methods to Implement Dark Mode in React Native
Posted by Zain Sajjad on August 13, 2021
The world today uses smartphones more than ever before. Through advancements in display technology, now it's possible to save more energy by switching to dark mode UI. Android 10 and iOS 13 brought native support of dark mode to the most used smartphones.
React Native developers always strive to deliver a top-notch user experience. With the support of dark mode in operating systems, it is now a necessity for most apps. Here we will look at different approaches to support dark mode in React Native apps.
Table of contents
Implement Dark Mode Using React Native Appearance
React Native has its in-built module Appearance that provides users’ theme preferences. Here is a small implementation of this module to get you familiar with how it works:
const [theme, setTheme] = useState<ColorSchemeName>();
const themeChangeListener = useCallback(() => {
setTheme(Appearance.getColorScheme());
}, []);
useEffect(() => {
Appearance.addChangeListener(themeChangeListener);
return () => Appearance.removeChangeListener(themeChangeListener);
}, [themeChangeListener]);
console.log({ theme });
This code can be avoided by using a simple hook provided by Appearance modules: useColorScheme(): 'light' | 'dark'
. This hook implements the event listener and triggers a re-render when the user switches the OS's UI mode. Here is how a simple component will look like when implementing useColorScheme
:
import {Text, useColorScheme} from 'react-native';
const Label: React.FC<LabelProps> = ({
...props
}) => {
const theme = useColorScheme();
return (
<Text
style={[
theme === 'dark' ? style.dark : style.light,
]}
numberOfLines={2}>
{props.label}
</Text>
);
};
export default Label;
Implement Dark Mode Using React Navigation
Deciding about a navigation library is one of the most discussed topics in the React Native community. One of the top advantages of React Navigation is theme support. This offloads the implementation of making themes from developers.
The `NavigationContainer` of React Navigation allows you to pass the `theme` prop. You can change the theme prop dynamically and all the components will automatically update to reflect the new theme.
Here is what React Navigation's theme looks like:
import { useColorScheme } from 'react-native';
import {
NavigationContainer,
DefaultTheme,
DarkTheme,
} from '@react-navigation/native';
export default () => {
const scheme = useColorScheme();
return (
<NavigationContainer theme={scheme === 'dark' ? DarkTheme : DefaultTheme}>
{/* content */}
</NavigationContainer>
);
};
Once provided to NavigationContainer
any component can use the theme from the useTheme()
hook. Here is an example:
import * as React from 'react';
import { TouchableOpacity, Text } from 'react-native';
import { useTheme } from '@react-navigation/native';
// Black background and white text in light theme, inverted on dark theme
function MyButton() {
const { colors } = useTheme();
return (
<TouchableOpacity style={{ backgroundColor: colors.card }}>
<Text style={{ color: colors.text }}>Button!</Text>
</TouchableOpacity>
);
}
Implement Dark Mode Using Styled-Components
Styled-components is one of the most trusted and widely used libraries for styling with React applications. Styled-components also support React Native with all of its awesome features set.
Styled-components has full theming support by exporting a <ThemeProvider>
wrapper component. Just like any other react context provider, it provides a theme to all React components underneath itself. In the render tree, all styled-components will have access to the provided theme. We can use it with React Native Appearance's hook at the top of our component tree. Here is how we can provide the theme to all child components:
import { useColorScheme } from 'react-native';
import { ThemeProvider } from 'styled-components';
const darkTheme = {
background: "#1A1A1A",
foreground: "#FAFAFA"
};
const lighTheme = {
background: "#FAFAFA",
foreground: "#1A1A1A",
};
const App: React.FC = () => {
const scheme = useColorScheme();
return (
<ThemeProvider theme={scheme === 'dark' ? darkTheme : lightTheme}>
{props.children}
</ThemeProvider>
);
}
export default App;
And then any component using styled-components will have access to the theme as follows:
import styled from 'styled-components/native'
const Button = styled.TouchableOpacity`
color: ${props => props.theme.foreground};
background-color: ${props => props.theme.background};
`;
export default Button;
This method avoids the inline styling approach that was used in all previous methods. It can make our JSX look a lot cleaner, and also more maintainable as all style-related items will go in style declaration.
Implement Dark Mode Using Emotion Native
Emotion is also one of the giants in the CSS-in-JS arena and also supports React Native. That makes it a great choice for developers working on a solution that supports both mobile and web platforms.
Like styled-components, emotion also avoids the inline styling approach for theme support. It has a similar ThemeProvider
that can help you supply a theme to all child components. The syntax here looks quite the same as for styled-components:
import { useColorScheme } from 'react-native';
import { ThemeProvider } from '@emotion/react'; // Here is the change
const darkTheme = {
background: "#1A1A1A",
foreground: "#FAFAFA"
};
const lighTheme = {
background: "#FAFAFA",
foreground: "#1A1A1A",
};
const App: React.FC = () => {
const scheme = useColorScheme();
return (
<ThemeProvider theme={scheme === 'dark' ? darkTheme : lightTheme}>
{props.children}
</ThemeProvider>
);
}
export default App;
And some minor changes in component:
import { styled } from '@emotion/native'
const Button = styled.TouchableOpacity`
color: ${props => props.theme.foreground};
background-color: ${props => props.theme.background};
`;
export default Button;
Implement Dark Mode Using React Native Paper
Several UI libraries are available for React Native developers today. One of the most prominent is React Native Paper, a cross-platform material design for React Native. It is a collection of customizable and production-ready components for React Native, following Google’s Material Design guidelines. With 30+ customizable components, it is a great choice to go with Material UI.
React Native Paper comes with theming support. Themes provided at the top of the component tree are used across all components.
import * as React from 'react';
import { useColorScheme } from 'react-native';
import { DefaultTheme, Provider as PaperProvider } from 'react-native-paper';
import App from './src/App';
const darkTheme = {
...DefaultTheme,
roundness: 2,
colors: {
...DefaultTheme.colors,
primary: "#1A1A1A",
accent: "#FAFAFA"
},
};
const lightTheme = {
...DefaultTheme,
roundness: 2,
colors: {
...DefaultTheme.colors,
primary: "#FAFAFA",
accent: "#1A1A1A",
},
};
export default function Main() {
const scheme = useColorScheme();
return (
<PaperProvider theme={scheme === 'dark' ? darkTheme : lightTheme}>
<App />
</PaperProvider>
);
}
Also, the theme is available via the useTheme
hook:
import * as React from 'react';
import { useTheme } from 'react-native-paper';
function MyComponent(props) {
const { colors } = useTheme();
return <Text style={{ color: colors.primary }}>Yo!</Text>;
}
Wrapping it Up
Dark mode is one of the essential features of apps today. As developers, we are always in pursuit of the finest solutions that are scalable and easy to maintain. The libraries discussed above provide great support for dark mode and are quite easy to implement in conjunction with ButterCMS as your React Native CMS. Feel free to let us know which library you are using for dark mode in your next unicorn app using the comments below.
ButterCMS is the #1 rated Headless CMS
Related articles
Don’t miss a single post
Get our latest articles, stay updated!
Zain is a Senior Frontend Engineer at Peekaboo Guru. He is passionate about anything and everything React, React Native, JavaScript & Mobile Machine Learning.