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
Let’s assume you’ve built the following React Native application that adds and removes items off of a to-do list:
react-native-safe-area-context
Uploaded by Raphael Ugwu on 2021-08-30.
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:
react-native-safe-area-context-2
Uploaded by Raphael Ugwu on 2021-08-30.
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.
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:
react-native-safe-area-context-3
Uploaded by Raphael Ugwu on 2021-08-31.
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.
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.
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 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.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to implement one-way and two-way data binding in Vue.js, using v-model and advanced techniques like defineModel for better apps.
Compare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.