5 Easy Methods to Implement Dark Mode in React Native

Posted by Zain Sajjad on August 13, 2021

GSD

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.

Implement Dark Mode Using React Native Appearance

React Native logo

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;

Headless CMS for React Native: See Why ButterCMS is Rated #1

Implement Dark Mode Using React Navigation

React Navigation logo

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 logo

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.

Headless CMS for React Native: See Why ButterCMS is Rated #1

Implement Dark Mode Using Emotion Native

undefined

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

Paper logo

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>;
    }

Illustration: React Native switch

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.

 

Receive tutorials and updates to keep your work running smoothly.
    
Zain Sajjad

Zain is a Senior Frontend Engineer at Peekaboo Guru. He is passionate about anything and everything React, React Native, JavaScript & Mobile Machine Learning.

ButterCMS is the #1 rated Headless CMS

Related articles

Don’t miss a single post

Get our latest articles, stay updated!