If you’ve been building React Native apps for a while, SafeAreaView from react-native has probably been your go-to for handling notches, status bars, and home indicators. It works — until it doesn’t. As of React Native 0.73+, the core SafeAreaView is on its way out, and for good reason. Here’s everything you need to know about why it’s deprecated and what you should be using today.
Why SafeAreaView Is Being Deprecated
The original SafeAreaView shipped as part of React Native core and was fine for simple cases. But it came with a pile of known issues that were never really fixed:
- iOS-only by design. On Android,
SafeAreaViewdoes essentially nothing. It doesn’t account for the status bar, camera cutouts, or gesture navigation insets. This led to countless developers wrapping it with platform-specific hacks. - No programmatic control. You couldn’t read the inset values yourself to apply them conditionally or pass them to child components.
- No support for edges. You couldn’t say “only apply safe area padding to the bottom” — it was all or nothing.
- Flicker on mount. Because it reads insets asynchronously on iOS, you’d sometimes see a layout jump when the component first rendered.
The React Native team acknowledged these limitations and, rather than patching a broken foundation, pointed developers toward the community solution that already solved all of these problems.
The Replacement: react-native-safe-area-context
The library react-native-safe-area-context is now the official recommended approach. It’s maintained by the Expo team, ships with Expo out of the box, and is used internally by React Navigation. If you’re already using React Navigation, you likely already have it installed.
# npm
npm install react-native-safe-area-context
# yarn
yarn add react-native-safe-area-context
# expo
npx expo install react-native-safe-area-context
For bare React Native projects, you’ll also need to run:
cd ios && pod installSetting It Up
The library requires a SafeAreaProvider at the root of your app. This is what reads the device’s inset values and makes them available to all children via context.
// App.tsx
import { SafeAreaProvider } from 'react-native-safe-area-context';
import SettingsScreen from './screens/SettingsScreen';
export default function App() {
return (
<SafeAreaProvider>
<SettingsScreen />
</SafeAreaProvider>
);
}
You only need one SafeAreaProvider — place it at the very top of your tree and forget about it.
Option 1: SafeAreaView (from the library, not core)
The library ships its own SafeAreaView that actually works on both iOS and Android, supports edge selection, and doesn’t flicker.
// Before (deprecated core import)
import { SafeAreaView } from 'react-native';
// After (correct import)
import { SafeAreaView } from 'react-native-safe-area-context';
const SettingsScreen = () => {
return (
<SafeAreaView style={{ flex: 1, backgroundColor: '#1c1f27' }}>
{/* your content */}
</SafeAreaView>
);
};
The drop-in replacement is that simple for most screens. The key difference is this one actually handles Android status bars, camera punch-holes, and bottom gesture bars correctly.
Controlling Edges
One of the biggest upgrades is the edges prop. You can now specify exactly which sides should apply safe area padding:
// Only pad top and bottom, ignore left/right
<SafeAreaView edges={['top', 'bottom']} style={{ flex: 1 }}>
...
</SafeAreaView>
// Only pad the bottom (useful for tab bars)
<SafeAreaView edges={['bottom']} style={{ flex: 1 }}>
...
</SafeAreaView>
This is huge for things like bottom tab bars, modals, and full-bleed headers where you want safe area on some sides but not others.
Option 2: useSafeAreaInsets (for full control)
Sometimes you don’t want a wrapping component — you want the raw inset numbers so you can apply them yourself. The useSafeAreaInsets hook gives you exactly that.
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { View, ScrollView, StyleSheet } from 'react-native';
const SettingsScreen = () => {
const insets = useSafeAreaInsets();
return (
<View style={{ flex: 1, backgroundColor: '#1c1f27' }}>
<ScrollView
contentContainerStyle={{
paddingTop: insets.top,
paddingBottom: insets.bottom + 16,
paddingLeft: insets.left,
paddingRight: insets.right,
}}
>
{/* your content */}
</ScrollView>
</View>
);
};
This is the most flexible approach. It’s especially useful when:
- You want to add the inset as padding on a
ScrollView‘scontentContainerStyleinstead of the outer wrapper (so content scrolls under a translucent nav bar, for example) - You need to pass inset values into animated styles
- You’re building a custom header or bottom sheet that needs to know the exact values
Real-world example: status bar height in a custom header
const Header = () => {
const insets = useSafeAreaInsets();
return (
<View
style={{
paddingTop: insets.top + 8,
paddingHorizontal: 20,
paddingBottom: 12,
backgroundColor: '#1c1f27',
}}
>
<Text style={{ fontSize: 28, fontWeight: '700', color: '#fff' }}>
Settings
</Text>
</View>
);
};
No magic, no flickering, no platform hacks — just the number you need, applied exactly where you want it.
Option 3: useSafeAreaFrame
Less commonly needed but worth knowing — useSafeAreaFrame gives you the frame dimensions of the safe area itself:
import { useSafeAreaFrame } from 'react-native-safe-area-context';
const MyComponent = () => {
const frame = useSafeAreaFrame();
// frame.width, frame.height, frame.x, frame.y
};
This is useful if you’re doing layout calculations that depend on the available screen real estate after insets are applied — for example, in a custom carousel or a full-screen map.
Migrating an Existing Screen
Here’s a before/after for a typical settings screen migration:
Before:
import { SafeAreaView, StatusBar, StyleSheet } from 'react-native';
const SettingsScreen = () => (
<SafeAreaView style={styles.safe}>
<StatusBar barStyle="light-content" backgroundColor="#1c1f27" />
{/* content */}
</SafeAreaView>
);
const styles = StyleSheet.create({
safe: {
flex: 1,
backgroundColor: '#1c1f27',
},
});
After:
import { StatusBar } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
const SettingsScreen = () => (
<SafeAreaView style={{ flex: 1, backgroundColor: '#1c1f27' }} edges={['top', 'bottom']}>
<StatusBar barStyle="light-content" backgroundColor="#1c1f27" />
{/* content */}
</SafeAreaView>
);
The surface-level change is tiny — just the import. But under the hood you get Android support, edge control, no flicker, and access to insets programmatically if you ever need them.
Quick Reference
| Need | Use |
|---|---|
| Simple full-screen layout | SafeAreaView from react-native-safe-area-context |
| Padding on specific sides only | SafeAreaView with edges prop |
| Raw inset values for custom layouts | useSafeAreaInsets() |
| Insets on a ScrollView’s content | useSafeAreaInsets() + contentContainerStyle |
| Screen dimensions after insets | useSafeAreaFrame() |
Pros and Cons
Core SafeAreaView (deprecated)
Pros
- Zero setup — no extra package to install
- Familiar to anyone who learned React Native before 0.73
- Works fine for simple iOS-only apps with no custom layout needs
Cons
- Does nothing on Android — status bar, camera cutouts, and gesture insets are completely ignored
- No
edgesprop — you can’t target specific sides - No programmatic access to inset values
- Causes a visible layout flicker on first render on iOS
- Actively deprecated — expect it to be removed in a future major release
- Forces you into platform-specific workarounds (
Platform.OS === 'android') that bloat your components
SafeAreaView from react-native-safe-area-context
Pros
- Works correctly on both iOS and Android out of the box
edgesprop gives you fine-grained control over which sides are padded- No flicker — insets are read synchronously via context
- Backed by Expo and used internally by React Navigation, so it’s well maintained and battle-tested
- Drop-in replacement — changing the import is often all you need
Cons
- Requires installing an extra package and running
pod installon bare RN - Requires
SafeAreaProviderat the root of your tree — forgetting it causes a silent fallback to zero insets, which can be confusing to debug - Still a wrapper component, so you can’t access raw values without reaching for the hook
useSafeAreaInsets()
Pros
- Full programmatic control — apply insets exactly where you need them, not just on an outer wrapper
- Works seamlessly with
ScrollView‘scontentContainerStylefor scroll-under effects behind translucent headers or tab bars - Composes naturally with
AnimatedandReanimatedfor inset-aware animations - Keeps your component tree flatter — no extra wrapper
Viewneeded
Cons
- More verbose than a component — you have to manually apply each inset value
- Easy to forget a side (e.g.
insets.bottom) and ship a screen with content hidden behind the home indicator - Only works inside a
SafeAreaProvider— calling it outside throws at runtime
useSafeAreaFrame()
Pros
- Gives accurate layout dimensions after insets are subtracted — useful for carousels, maps, and full-screen canvases
- Reactive — updates automatically on orientation change
Cons
- Rarely needed in typical app development; most layouts don’t require frame-level precision
- Can be overkill if you only need one inset value —
useSafeAreaInsets()is almost always the better choice
The Bottom Line
The core SafeAreaView from react-native was always a leaky abstraction — iOS-only, inflexible, and prone to layout jumps. The react-native-safe-area-context library solves every one of those problems and gives you a proper hook-based API for when you need more control.
If you’re starting a new project, reach for react-native-safe-area-context from day one. If you’re on an existing codebase, the migration is mostly a find-and-replace on your import line — and your Android users will quietly thank you for it.






