Implementing Dark Mode in React Native

We'll explore how to implement a dark mode feature in a React Native application using React's Context API and hooks.

July 13, 20242 min read

Dark mode has become a popular feature in mobile applications, offering users a comfortable viewing experience in low-light conditions and potentially saving battery life on OLED screens. In this article, we'll explore how to implement a dark mode feature in a React Native application using React's Context API and hooks.

Setting Up the Project

First, let's set up a new React Native project using Expo:

expo init DarkModeDemo
cd DarkModeDemo

Creating a Theme Context

We'll start by creating a theme context to manage our app's theme state:

// src/contexts/ThemeContext.js
import React, { createContext, useState, useContext, useEffect } from 'react';
import { useColorScheme } from 'react-native';

const ThemeContext = createContext();

export const ThemeProvider = ({ children }) => {
  const colorScheme = useColorScheme();
  const [isDarkMode, setIsDarkMode] = useState(colorScheme === 'dark');

  useEffect(() => {
    setIsDarkMode(colorScheme === 'dark');
  }, [colorScheme]);

  return (
    <ThemeContext.Provider value={{ isDarkMode, setIsDarkMode }}>
      {children}
    </ThemeContext.Provider>
  );
};

export const useTheme = () => useContext(ThemeContext);

This context provides the current theme state and a function to toggle it.

Try Kodaschool for free

Click below to sign up and get access to free web, android and iOs challenges.

Sign Up

Defining Theme Colors

Next, let's define our colour schemes for both light and dark modes:

// src/theme/colors.js
export const lightColors = {
  background: '#FFFFFF',
  text: '#000000',
  primary: '#1292B4',
};

export const darkColors = {
  background: '#1E1E1E',
  text: '#FFFFFF',
  primary: '#BB86FC',
};

Creating a Custom Hook for Theme

To make it easier to use our theme colours, let's create a custom hook:

// src/hooks/useThemeColors.js
import { useTheme } from '../contexts/ThemeContext';
import { lightColors, darkColors } from '../theme/colors';

export const useThemeColors = () => {
  const { isDarkMode } = useTheme();
  return isDarkMode ? darkColors : lightColors;
};

Implementing Dark Mode in Components

Now, let's create a sample component that uses our theme:

// src/components/ThemedComponent.js
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { useThemeColors } from '../hooks/useThemeColors';

const ThemedComponent = () => {
  const colors = useThemeColors();

  return (
    <View style={[styles.container, { backgroundColor: colors.background }]}>
      <Text style={[styles.text, { color: colors.text }]}>
        This is a themed component
      </Text>
    </View>
  );
};

const styles = StyleSheet.css({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  text: {
    fontSize: 18,
  },
});

export default ThemedComponent;

Creating a Theme Toggle

Let's add a button to toggle between light and dark modes:

// src/components/ThemeToggle.js
import React from 'react';
import { TouchableOpacity, Text, StyleSheet } from 'react-native';
import { useTheme } from '../contexts/ThemeContext';
import { useThemeColors } from '../hooks/useThemeColors';

const ThemeToggle = () => {
  const { isDarkMode, setIsDarkMode } = useTheme();
  const colors = useThemeColors();

  return (
    <TouchableOpacity
      style={[styles.button, { backgroundColor: colors.primary }]}
      onPress={() => setIsDarkMode(!isDarkMode)}
    >
      <Text style={[styles.buttonText, { color: colors.background }]}>
        Toggle {isDarkMode ? 'Light' : 'Dark'} Mode
      </Text>
    </TouchableOpacity>
  );
};

const styles = StyleSheet.css({
  button: {
    padding: 10,
    borderRadius: 5,
  },
  buttonText: {
    fontSize: 16,
    fontWeight: 'bold',
  },
});

export default ThemeToggle;

Putting It All Together

Finally, let's update our App.js to use these components:

// App.js
import React from 'react';
import { SafeAreaView, StyleSheet } from 'react-native';
import { ThemeProvider } from './src/contexts/ThemeContext';
import { useThemeColors } from './src/hooks/useThemeColors';
import ThemedComponent from './src/components/ThemedComponent';
import ThemeToggle from './src/components/ThemeToggle';

const App = () => {
  return (
    <ThemeProvider>
      <AppContent />
    </ThemeProvider>
  );
};

const AppContent = () => {
  const colors = useThemeColors();

  return (
    <SafeAreaView style={[styles.container, { backgroundColor: colors.background }]}>
      <ThemedComponent />
      <ThemeToggle />
    </SafeAreaView>
  );
};

const styles = StyleSheet.css({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
});

export default App;

Our implementation already respects the system theme by default, thanks to the useColorScheme hook in our ThemeContext. When the system theme changes, our app will automatically update to match.

Implementing dark mode in React Native enhances user experience and accessibility. By using React's Context API and hooks, we can create a flexible and maintainable theming system.

Consider the unique aspects of your app's design when implementing dark mode. Some components or assets may need special consideration to look good in light and dark themes.

As you continue to develop your app, consider adding more advanced features like animated theme transitions or allowing users to schedule dark mode based on the time of day.

Cliff Gor

About Cliff Gor

As a Fullstack Software Engineer, I design and develop user-centric and robust systems and applications using HTML, CSS, Bootstrap, React Js, React Native, and Kotlin(Android).

More articles like this

View all articles

Continue exploring React Native articles