iOS dark mode with React Native
Tags: react, reactnative, ios, tutorial
Published: 2020-10-20 00:20:46

# Introduction
iOS 13 introduced Dark Mode, a feature that lets users choose between a system-wide light or dark theme. At that time many people saw this more as a gimmick. Soon after, Whatsapp, Facebook Messenger, Youtube, or Gmail came up with both light and dark versions of their iOS apps.

![Facebook Messenger Dark vs Light version](https://dev-to-uploads.s3.amazonaws.com/i/lokmicmg8514hul3q5o5.jpg)
                               _Image from The Verge_

## Start from here

Before designing your app, make sure you read [Apple Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/dark-mode). I will not go into design details but [here](https://blog.prototypr.io/designing-a-dark-mode-for-your-ios-app-the-ultimate-guide-6b043303b941) is a great resource. 

Key takeaways:
* iOS provides dynamic system colors that automatically adapt to light or dark modes.
* your app should comply with the appearance mode people choose in Settings.


## Let's build an app

I will create a new application using [React Native CLI](https://reactnative.dev/docs/0.60/getting-started). You can follow along or check the final version on [Github] (https://github.com/cristian-rita/dark-mode-example-ios). 

```Bash
npx react-native init DarkModeExample
``` 

Make sure you remove generated code from App.js and replace it with the following  

```javascript
import React from 'react';
import {View, Text, StyleSheet} from 'react-native';

const App = () => (
  <View style={styles.container}>
    <Text>Hello World</Text>
  </View>
);

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

export default App;
```

Now we are ready to customize the app.

### 1. Launch Screen

Let's start the customization with the Launch Screen. This is the splash screen which appears for a few moments when the application is launched.

Open the project in Xcode. 
![Xcode Project](https://dev-to-uploads.s3.amazonaws.com/i/0o7ry4baj8r537ngljco.png)

Go to **LaunchSreen.storyboard** and make sure you change the background color of the View to SystemBackground. SystemBackground is pure white for the light theme and pure black for the dark one. I also changed the color of the "DarkModeExample" text to System Orange Color.

To see the result, on your simulator, go to Settings->Developer->Appearance, switch between dark and light appearance, and open the app. Depending on what you selected, the LaunchScreen should change accordingly.   
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/xmg8a5jlegpb8hrlja8j.png)

### 2. Add a login screen

For demonstration purposes, we will design a login screen.
Many UI kits and libraries offered theming capabilities even before Dark Mode landed on iOS. Most of them rely on [React Context](https://reactjs.org/docs/context.html) to provide this kind of functionality.

React Native 0.63 introduced [PlatformColor](https://reactnative.dev/docs/platformcolor). PlatformColor lets you access native colors on the target platform by supplying the native color’s corresponding string value. 

```javascript
backgroundColor: PlatformColor('systemBackground')
```

_systemBackground_ is a native iOS color. More than that it is dynamic which means its value is #fff for the light theme and #000 for the dark one. The color will automatically change when the theme is changed by the user in Settings. 

Now let's update the App.js file:

```javascript
import React from 'react';
import {
  KeyboardAvoidingView,
  Platform,
  PlatformColor,
  Pressable,
  StyleSheet,
  Text,
  TextInput,
  View,
} from 'react-native';

const App = () => {
  return (
    <View style={styles.container}>
      <KeyboardAvoidingView
        style={styles.contentContainer}
        behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
        <View style={styles.form}>
          <TextInput
            paddingLeft={10}
            autoCapitalize="none"
            autoCorrect={false}
            keyboardType="email-address"
            placeholder="Email"
            placeholderTextColor={Platform.select({
              ios: PlatformColor('secondaryLabel'),
              android: 'white',
            })}
            style={styles.input}
          />

          <TextInput
            paddingLeft={10}
            secureTextEntry
            autoCapitalize="none"
            autoCorrect={false}
            placeholder="Password"
            placeholderTextColor={Platform.select({
              ios: PlatformColor('secondaryLabel'),
            })}
            style={styles.input}
          />

          <View>
            <Pressable style={styles.loginButton}>
              <Text style={styles.label}>Login</Text>
            </Pressable>
          </View>
        </View>
      </KeyboardAvoidingView>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    ...Platform.select({
      ios: {backgroundColor: PlatformColor('systemBackground')},
      default: {
        backgroundColor: '#000000ff',
      },
    }),
  },
  contentContainer: {
    flex: 1,
    maxHeight: '90%',
    flexDirection: 'column',
    justifyContent: 'space-evenly',
    alignItems: 'center',
    ...Platform.select({
      ios: {backgroundColor: PlatformColor('systemBackground')},
      default: {
        backgroundColor: '#000000ff',
      },
    }),
  },
  form: {
    width: '90%',
    justifyContent: 'space-between',
    borderRadius: 5,
  },
  input: {
    height: 40,
    marginTop: 10,
    fontWeight: '500',
    borderWidth: 0.3,
    borderRadius: 5,
    ...Platform.select({
      ios: {
        color: PlatformColor('labelColor'),
        backgroundColor: PlatformColor('tertiarySystemBackground'),
        borderColor: PlatformColor('separator'),
      },
      default: {
        backgroundColor: '#1c1c1eff',
        borderColor: '#54545899',
      },
    }),
  },
  loginButton: {
    width: '100%',
    justifyContent: 'center',
    borderRadius: 5,
    height: 40,
    marginTop: 40,
    ...Platform.select({
      ios: {backgroundColor: PlatformColor('systemBlue')},
      android: {backgroundColor: '#0a84ffff'},
      default: {
        backgroundColor: '#0a84ffff',
      },
    }),
  },
  label: {
    fontWeight: '600',
    color: 'white',
    width: '100%',
    fontSize: 20,
    textAlign: 'center',
  },
});

export default App;
```

_Note: Chances are this will not be responsive and won't look good on every screen size_

Switch again between light and dark themes and see how colors are automatically updated. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/qx71m19mp6hmggufceh4.png)



As you can see I used **PlatformColor** to get different iOS native colors. For a full list check [this](https://developer.apple.com/documentation/uikit/uicolor/ui_element_colors).

```javascript
PlatformColor('systemBlue');
```

**These colors are only available on iOS 13+ so for earlier versions of iOS or for the Android platform we should provide alternative values.** This can be accomplished using **PlatformSelect**.

```javascript
...Platform.select({
      ios: {backgroundColor: PlatformColor('systemBlue')},
      android: {backgroundColor: '#0a84ffff'},
      default: {
        backgroundColor: '#0a84ffff',
      },
    })
```
 
### 3. Add dynamic logo

The only missing part of our login screen is the logo which is usually an image. There is a pretty high chance our logo will not look good on both black and white backgrounds. To fix this we will need a light and a dark version for the logo.

First, we will create the following folder structure

```
DarkModeExample
│   
│   
│
└───src
    └───assets
    │    │   logo_dark.png
    │    │   logo_light.png
    │    │ 
    └─── components
          │  LogoComponent.js
          │
```

You can get the images from my [Github repository](https://github.com/cristian-rita/dark-mode-example-ios).

Now let's implement the LogoComponent.

```javascript
import React from 'react';
import {useColorScheme, Image} from 'react-native';

const LogoComponent = (props) => {
  const colorScheme = useColorScheme();

  return colorScheme === 'dark' ? (
    <Image source={require('./../assets/logo_dark.png')} style={{...props}} />
  ) : (
    <Image source={require('./../assets/logo_light.png')} style={{...props}} />
  );
};

export default LogoComponent;
``` 

As you can see, React Native provides **useColorScheme()** hook which returns the active color scheme as a string. Based on that we return the proper version of the logo.
 
All we need to do is to import the LogoComponent in our App.js and render it just above the form. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/jbg7i7k6sl5n21s64oi9.png)


## Conclusion
You made it here! Thank you! At this point, you should be able to implement **Dark Mode** into your React Native app 🚀