Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

The safe area of the screen

On modern phones the screen covers the whole front panel: the camera notch and the status bar with the clock at the top, the gesture bar at the bottom. The safe area is the part of the screen nothing covers. Content, especially text and buttons, should stay inside it.

When you need it

The Expo Router stack header and tab bar take the safe area into account by themselves. The problem appears on screens without a header. Hide the header in the to-do list:

// app/_layout.tsx
import { Stack } from 'expo-router';
 
export default function RootLayout() {
  return <Stack screenOptions={{ headerShown: false }} />;
}

Now the "My tasks" heading ends up under the clock and the notch on an iPhone, and under the status bar on Android.

SafeAreaView

The SafeAreaView component from the react-native-safe-area-context package adds padding exactly the size of the system elements. The package is already installed in the Expo template.

// app/index.tsx
import { Text } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
 
export default function Index() {
  return (
    <SafeAreaView style={{ flex: 1, backgroundColor: '#fff' }}>
      <Text style={{ fontSize: 24, padding: 16 }}>My tasks</Text>
    </SafeAreaView>
  );
}

The heading moves below the clock and the notch. The white background still fills the whole screen, including the area under the status bar, because the padding is added inside.

The edges prop chooses the sides: edges={['top']} adds padding only at the top. This is needed when a tab bar at the bottom already handles the bottom area.

useSafeAreaInsets

The hook returns the insets as numbers, which you can use in any style. For example, to pin the "Add" button to the bottom of the screen above the gesture bar:

// app/index.tsx
import { View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import AppButton from '@/components/AppButton';
 
export default function Index() {
  const insets = useSafeAreaInsets();
 
  return (
    <View style={{ flex: 1, paddingTop: insets.top, paddingBottom: insets.bottom + 8 }}>
      <View style={{ flex: 1 }} />
      <AppButton title="Add task" onPress={() => {}} />
    </View>
  );
}

insets holds four numbers: top, bottom, left and right. On an iPhone with a notch top is about 50 to 60; on older phones with a Home button, about 20.

The provider

Safe area components work inside a SafeAreaProvider. Expo Router adds it itself, so in Expo Router projects you do not need to wrap the app by hand.

Common mistake

Importing SafeAreaView from react-native. That old component works only on iOS, does nothing on Android and is deprecated in React Native 0.81. Import SafeAreaView from react-native-safe-area-context.

Thanks for reading!

Donate to the site — we work hard for you.