DEV Community

Cover image for Managing Themes in React Native Using Context API
Amit Kumar
Amit Kumar

Posted on

Managing Themes in React Native Using Context API

In modern React Native apps, managing global states like themes can be cumbersome if not done efficiently. One of the most elegant solutions for managing application-wide states is the Context API. In this article, we’ll explore how to implement dark and light themes in a React Native app using the Context API and React Native’s Appearance module.

Why Use the Context API?

The Context API provides a way to pass data through the component tree without having to pass props down manually at every level. For global themes, this is an ideal choice as it helps manage the UI’s look and feel across the entire app seamlessly.

Step-by-Step Guide

1. Setting Up the ThemeContext
We’ll first create a ThemeContext to store and manage our theme data. This will allow us to access and modify the theme throughout the app.

Create a ThemeContext in your src/contexts/ThemeContext/index.js:

import { Appearance } from 'react-native';
import React, {
  createContext,
  useCallback,
  useEffect,
  useMemo,
  useState,
} from 'react';

export const ThemeContext = createContext({});

export const ThemeConsumer = ({ children }) => (
  <ThemeContext.Consumer>
    {(context) => {
      if (context === undefined) {
        throw new Error('ThemeConsumer must be used within a ThemeProvider');
      }
      return children(context);
    }}
  </ThemeContext.Consumer>
);

export const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState(Appearance.getColorScheme());

  const setAppMode = () => {
    setTheme(Appearance.getColorScheme());
  };

  const themeChangeListener = useCallback(() => {
    setTheme(Appearance.getColorScheme());
  }, []);

  useEffect(() => {
    const themeListener = Appearance.addChangeListener(themeChangeListener);
    return () => themeListener.remove();
  }, [themeChangeListener]);

  const MemoizedValue = useMemo(() => ({
    theme,
    setAppMode,
  }), [theme]);

  return (
    <ThemeContext.Provider value={MemoizedValue}>
      {children}
    </ThemeContext.Provider>
  );
};

Enter fullscreen mode Exit fullscreen mode

2. Wrapping the App with ThemeProvider

To ensure the theme is accessible throughout the app, wrap the root of your app with ThemeProvider in App.js:

import React from 'react';
import { Provider } from 'react-redux'; // Assuming you're using Redux
import { ThemeProvider } from './src/contexts/ThemeContext';

const App = () => {
  return (
    <Provider store={store}>
      <ThemeProvider>
        {/* Other components */}
      </ThemeProvider>
    </Provider>
  );
};

export default App;

Enter fullscreen mode Exit fullscreen mode

Now, the entire app has access to the theme and can react to changes in theme mode.

3. Using the ThemeContext in Components

Let’s modify a component to dynamically change based on the theme.

import React, { useContext } from 'react';
import { StatusBar } from 'react-native';
import { ThemeContext } from '../src/contexts/ThemeContext';

const MyComponent = () => {
  const { theme = 'light' } = useContext(ThemeContext);

  return (
    <StatusBar barStyle={theme === 'dark' ? 'light-content' : 'dark-content'} />
  );
};

export default MyComponent;

Enter fullscreen mode Exit fullscreen mode

In this example, the StatusBar changes its barStyle based on whether the app is in dark mode or light mode.

4. Applying Theme to the Entire UI

You can apply the theme to other components to change their styles dynamically:

import React, { useContext } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { ThemeContext } from '../src/contexts/ThemeContext';

const ThemedView = () => {
  const { theme } = useContext(ThemeContext);

  return (
    <View style={theme === 'dark' ? styles.darkContainer : styles.lightContainer}>
      <Text style={theme === 'dark' ? styles.darkText : styles.lightText}>
        Hello, {theme === 'dark' ? 'Dark' : 'Light'} Theme!
      </Text>
    </View>
  );
};

const styles = StyleSheet.create({
  darkContainer: {
    backgroundColor: '#333',
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  lightContainer: {
    backgroundColor: '#fff',
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  darkText: {
    color: '#fff',
  },
  lightText: {
    color: '#000',
  },
});

export default ThemedView;

Enter fullscreen mode Exit fullscreen mode

Handling Theme Changes

When the device's appearance changes (e.g., when the user switches to dark mode), the Appearance API will trigger the themeChangeListener. This will automatically update the theme in your app.

Final Thoughts

The Context API, combined with React Native’s Appearance module, offers a simple and powerful way to handle themes in your app. This approach ensures that theme-related logic is centralized and easy to maintain. With this setup, you can quickly scale your theme system and apply it throughout your app.

Top comments (0)