Back to blog

Styling NativeBase theme based on color mode

Ankit Tailor· Software Engineer·August 10, 2022·3 min read
Styling NativeBase theme based on color mode

Modern applications need to support multiple color modes, including an easy switch between light and dark mode. This article is the complete guide to creating color modes in React Native apps with toggle features.

What we will cover

  1. How to set the initial color mode
  2. System-based color mode
  3. Getting system color mode using React Native
  4. Getting color mode using NativeBase
  5. Hooks and pseudo props to achieve color mode
  6. Toggling color mode
  7. Demo till now
  8. Persisting color mode

How to set the initial color mode

You can extend the default theme with config in NativeBaseProvider. The default color mode is light.

To set the initial color mode, pass initialColorMode in the config object. It will take the values assigned for light and dark. The initial color mode of the app will be the one passed as initialColorMode.

import React from "react";
import { NativeBaseProvider, extendTheme } from "native-base";
 
export function App({ children }) {
  const config = {
    initialColorMode: "dark", // initial color mode
  };
 
  const extendedTheme = extendTheme({ config });
 
  return (
    <NativeBaseProvider theme={extendedTheme}>
      {children}
    </NativeBaseProvider>
  );
}

System-based color mode

The useSystemColorMode prop sets the app's color mode based on the system. It uses the boolean value.

If useSystemColorMode is true, then the initial color mode will be the system color mode. Else, it will be light mode.

import React from "react";
import { NativeBaseProvider, extendTheme } from "native-base";
 
export function App({ children }) {
  const config = {
    useSystemColorMode: true, // Default system color mode
  };
 
  const extendedTheme = extendTheme({ config });
 
  return (
    <NativeBaseProvider theme={extendedTheme}>
      {children}
    </NativeBaseProvider>
  );
}

Getting system color mode using React Native

The Appearance module provides information about the user's appearance preferences.

You can get the desired color scheme using getColorScheme.

import { Appearance } from 'react-native';
 
const colorScheme = Appearance.getColorScheme();
console.log(colorScheme, 'color-scheme');
if (colorScheme === 'dark') {
  // Use dark color scheme
}

Getting color mode using NativeBase

NativeBase provides a hook to get the desired color mode.

const { colorMode } = useColorMode(); // light or dark

The applied scheme will be light or dark based on the current color mode.

Hooks and pseudo props for color mode

NativeBase provides the following props to get the format you want.

  1. useColorMode
  2. useColorModeValue
  3. _light
  4. _dark

useColorMode

This hook provides access to the current colorMode of the app and toggleColorMode.

const Example = () => {
  const { colorMode, toggleColorMode } = useColorMode();
  return (
    <Center
      flex={1}
      bg={colorMode === "light" ? "coolGray.50" : "coolGray.800"}
    >
      <Heading color={colorMode === "light" ? "coolGray.800" : "coolGray.50"}>
        {`Color mode is toggled to ${colorMode}.`}
      </Heading>
      <Pressable mt="10" onPress={toggleColorMode}>
        {colorMode === "light" ? (
          <MoonIcon color="coolGray.800" size="6" />
        ) : (
          <SunIcon color="coolGray.50" size="6" />
        )}
      </Pressable>
    </Center>
  );
};

In the above snippet, colorMode will be light or dark based on the current color mode. You can use the toggleColorMode function anywhere in the app to switch between the two modes.

useColorModeValue

This hook takes two arguments: values in light and values in dark.

const Example = () => {
  const { colorMode, toggleColorMode } = useColorMode();
  const bg = useColorModeValue('coolGray.50', 'coolGray.800');
  const color = useColorModeValue('coolGray.800', 'coolGray.50');
  const icon = useColorModeValue(
    <MoonIcon color="coolGray.800" size="6" />,
    <SunIcon color="coolGray.50" size="6" />
  );
 
  return (
    <Center flex={1} bg={bg}>
      <Heading color={color}>
        {`Color mode is toggled to ${colorMode}.`}
      </Heading>
      <Pressable mt="10" onPress={toggleColorMode}>
        {icon}
      </Pressable>
    </Center>
  );
};

In the above snippet, the useColorModeValue hook is taking light and dark mode values respectively.

_light and _dark

Styles passed in _light and _dark will be applied in their respective modes.

import React from 'react';
import { Icon, useColorMode, Pressable, Center, Heading } from 'native-base';
import { Feather } from '@expo/vector-icons';
 
export const Example = () => {
  const { colorMode, toggleColorMode } = useColorMode();
 
  return (
    <Center
      flex={1}
      w="full"
      _light={{ bg: 'coolGray.50' }}
      _dark={{ bg: 'coolGray.800' }}
    >
      <Heading
        _light={{ color: 'coolGray.800' }}
        _dark={{ color: 'coolGray.50' }}
      >
        {`Color mode is toggled to ${colorMode}.`}
      </Heading>
      <Pressable mt="10" onPress={toggleColorMode}>
        <Icon as={Feather} _light={{ name: 'moon' }} _dark={{ name: 'sun' }} />
      </Pressable>
    </Center>
  );
};

By default, NativeBase components are dark mode enabled.

Note: It's recommended to use _light and _dark props. If you want to use the useColorModeValue hook, you can use it at the top of the component.

Toggling color modes

You can use the useColorMode hook for toggling. It provides a toggleColorMode function that can be used to switch between the two modes.

const { toggleColorMode } = useColorMode();

Summary and Demo

It's time to see everything in action. To access the source code of the demo click here.

In the above demo code, the initial mode will be the system color mode.

The useColorMode hooks get the current color mode and toggle color mode.

The _light and _dark pseudo props apply styles based on color mode.

Persisting color mode

You can keep a color mode persistent by defining colorModeManager of type StorageManager in NativeBaseProvider. This will retain color mode even if the page is refreshed.

For Native

Use react-native-async-storage in native apps. It is asynchronous and a key-value storage for React Native applications.

import React from 'react';
import { NativeBaseProvider, StorageManager, ColorMode } from 'native-base';
import AsyncStorage from '@react-native-async-storage/async-storage';
 
const colorModeManager: StorageManager = {
  get: async () => {
    try {
      let val = await AsyncStorage.getItem('@color-mode');
      return val === 'dark' ? 'dark' : 'light';
    } catch (e) {
      return 'light';
    }
  },
  set: async (value: ColorMode) => {
    try {
      await AsyncStorage.setItem('@color-mode', value);
    } catch (e) {
      console.log(e);
    }
  },
};
export default function () {
  return (
    // pass it to NativeBaseProvider
    <NativeBaseProvider colorModeManager={colorModeManager}>
      {children}
    </NativeBaseProvider>
  );
}

For web

Use localStorage to store color mode in the web browser. It is a read-only property of the window interface. This provides access to a storage object for the document's origin. The data stored is saved across browser sessions.

import React from 'react';
import { ColorMode, NativeBaseProvider, StorageManager } from 'native-base';
const colorModeManager: StorageManager = {
  get: async () => {
    let val = localStorage.getItem('@color-mode');
    return val === 'dark' ? 'dark' : 'light';
  },
  set: async (value: ColorMode) => {
    let strValue = value ? value.toString() : '';
    localStorage.setItem('@color-mode', strValue);
  },
};
 
export default function () {
  return (
    <NativeBaseProvider colorModeManager={colorModeManager}>
      {children}
    </NativeBaseProvider>
  );
}

If you want to explore more, here are some examples of applications that were built using NativeBase. Check them out here: https://madewithnativebase.com/

Till next time then, ciao!