Raphael Ugwu Writer, software engineer, and a lifelong student.

Improve mobile UI with React Native safe-area-context

3 min read 1112

React Native Safe Area Context

When building a mobile application, a developer must repeatedly ensure that the content is rendered correctly on different types of screens. Time-consuming and error-prone, we can simplify this process using tools like  React Native’s safe-area-context API.

In this tutorial, we’ll use safe-area-context to position webpage content around irregular figures in a mobile application, for example, status bars, home indicators, and notches. To follow along with this tutorial, you should be familiar with React Native and React Hooks. Let’s get started!

Note: To follow along, you can check out the full codebase of the to-do list app used in this article

Mobile UI limitations

Let’s assume you’ve built the following React Native application that adds and removes items off of a to-do list:

Our to-do list app is functional and fairly easy to use. In a web view, our application wouldn’t look abnormal. However, you’ll quickly notice several problems with the mobile UI. For example, if a user tried to view the app in landscape mode, they would encounter the following error:

 

When a user turns their phone and tries to use landscape mode, our app’s header becomes partially blocked due to the mobile screen dimensions.

When adapting a web application for mobile screen dimensions, it is very common for certain elements to become blocked or distorted by mobile interface elements. Let’s improve the visual rendering of our header element using the safe-area-context API.

Getting started with safe-area-context

First, we’ll install safe-area-context in our project. Navigate to the root project of your folder and run the command below:

npm install react-native-safe-area-context

If you’re using React Native ≥v0.6.0, safe-area-context will be automatically linked to your project. However, if you’re using an earlier version of React Native, run the following command in the root folder of your project to link the library:

react-native link react-native-safe-area-context

safe-area-context uses two components to interact with React Native components, Providers and Consumers, which have a parent-child relationship. Consumers allow us to use the values that are made available by the nearest Provider.

In the safe-area-context library, only one component,SafeAreaProvider, serves as a Provider. Let’s wrap the root component of our app with the SafeAreaProvider component, which will provide the value or styles of any system element it overlaps to descendant consumers.

Add the code below to App.js:

import React from "react";
import { Text, View, StyleSheet, ScrollView, TouchableOpacity, KeyboardAvoidingView } from "react-native";
import { SafeAreaView, SafeAreaProvider } from "react-native-safe-area-context";
import Task from "./components/Tasks";

export default function App() {
  const [task, setTask] = useState();
  const [taskItems, setTaskItems] = useState([]);
  const handleAddTask = () => {
    Keyboard.dismiss();
    setTaskItems([...taskItems, task]);
    setTask(null);
  };
  const completeTask = (index) => {
    let itemsCopy = [...taskItems];
    itemsCopy.splice(index, 1);
    setTaskItems(itemsCopy);
  };
  return (
    <SafeAreaProvider>
      <ScrollView>
        <View style={styles.container}>
          <View style={styles.taskWrapper}>
            <Text style={styles.sectionTitle}>Today's Tasks</Text>

            <View style={styles.items}>
              {/* This is where the tasks will go   */}
              {taskItems.map((item, index) => {
                return (
                  <TouchableOpacity onPress={() => completeTask(index)}>
                    <Task key={index} text={item} />
                  </TouchableOpacity>
                );
              })}
            </View>
          </View>
          {/* Write a task*/}

          <KeyboardAvoidingView
            behavior={Platform.OS === "ios" ? "padding" : "height"}
            style={styles.writeTaskWrapper}
          >
            <TextInput
              style={styles.input}
              placeholder={"Write a task"}
              value={task}
              onChangeText={(text) => setTask(text)}
            />
            <TouchableOpacity onPress={() => handleAddTask()}>
              <View style={styles.addWrapper}>
                <Text style={styles.addText}>+</Text>
              </View>
            </TouchableOpacity>
          </KeyboardAvoidingView>
        </View>
      </ScrollView>
    </SafeAreaProvider>
  );
}

Next, we’ll wrap any element that we anticipate could be impacted by screen modifications in mobile devices with a Consumer component. In our case, we wrap the Text child component that contains the header of our to-do list app with SafeAreaView, the primary Consumer component in safe-area-context:

// ./App.js

import React from "react";
import { Text, View, StyleSheet, ScrollView } from "react-native";
import { SafeAreaView, SafeAreaProvider } from "react-native-safe-area-context";

export default function App() {
  return (
    <SafeAreaProvider>
      <ScrollView>
        <View style={styles.container}>

          ...

          <View style={styles.taskWrapper}>
            <SafeAreaView>
              <Text style={styles.sectionTitle}>Today's Tasks</Text>
            </SafeAreaView>
          </View>

          ...

        </View>
      </ScrollView>
    </SafeAreaProvider>
  );
}

SafeAreaView acts like a regular React Native View component, however, it includes additional padding and margins, positioning the enclosed component away from any mobile onscreen modifications.

SafeAreaView includes a prop for edges, allowing you to customize safe area insets around the edges of your component. edges takes values of top, right, bottom, and left, with all as the default.

Let’s see how our app’s header is positioned differently after adding the SafeAreaView component:

 

In the video above, our app’s header is more centralized. The extra styling provided by SafeAreaView prevents mobile screen modifications from blocking the header, regardless of the type of device or screen dimensions.



safe-area-context includes built-in testing with Jest. You can read more about testing in the docs.

Optimizing rendering with SafeAreaView

The SafeAreaView component can prevent rendering delays when a user rotates their device. Additionally, React Native uses a Provider property called initialWindowMetrics that you can use to speed up your application’s initial render. Let’s add initialWindowMetrics in App.js:

// ./App.js

import React from "react";
import { Text, View, StyleSheet, ScrollView } from "react-native";
import { SafeAreaView, SafeAreaProvider, InitialWindowMetrics } from "react-native-safe-area-context";

export default function App() {
  return (
    <SafeAreaProvider initialMetrics={initialWindowMetrics}>
      <ScrollView>
        <View style={styles.container}>

          ...

          <View style={styles.taskWrapper}>
            <SafeAreaView>
              <Text style={styles.sectionTitle}>Today's Tasks</Text>
            </SafeAreaView>
          </View>

          {/* Include TextInput and button for adding items here */}

        </View>
      </ScrollView>
    </SafeAreaProvider>
  );
}

The initialWindowsMetrics property is specified as the initialMetric prop. This property should only be used if your application’s provider doesn’t remount, or if your application isn’t making use of the React Navigation library.

Summary

Handling and managing data in mobile applications can be a challenge in comparison to web applications. However, tools like safe-area-context provide a pathway to building interfaces that easily adapt to different mobile screens, providing a uniform UX across different devices.

safe-area-context is easy to use by simply wrapping elements with the SafeAreaView component. While the default values are effective on their own, you can add custom values to padding, margin, and edges as you see fit for your application. I hope you enjoyed this tutorial!

LogRocket: Instantly recreate issues in your React Native apps.

LogRocket is a React Native monitoring solution that helps you reproduce issues instantly, prioritize bugs, and understand performance in your React Native apps.

LogRocket also helps you increase conversion rates and product usage by showing you exactly how users are interacting with your app. LogRocket's product analytics features surface the reasons why users don't complete a particular flow or don't adopt a new feature.

Start proactively monitoring your React Native apps — try LogRocket for free.

Raphael Ugwu Writer, software engineer, and a lifelong student.

Leave a Reply