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.