One Logo Pool Ball
wwwwwwwwwwwwwwwwwww

Safe Area

Keep content clear of the notch, status bar, and home indicator with One.UI.SafeArea

The safe area is the part of the screen that system UI does not cover: below the status bar and notch or Dynamic Island, above the home indicator or Android navigation bar, and clear of display cutouts. One.UI.SafeArea.View pads its content into that area, and useSafeAreaInsets gives you the raw numbers for custom layouts.

One mounts the safe area provider at the root of every app, so both work with no setup. Libraries that import react-native-safe-area-context, such as React Navigation, resolve to the same implementation and read the same values. For the component reference in the router docs, see SafeAreaView.

Usage

import { One, Slot } from 'one'
export default function Layout() {
return (
<One.UI.SafeArea.View style={{ flex: 1 }} edges={['top']}>
<Slot />
</One.UI.SafeArea.View>
)
}

edges={['top']} pads only the top, the usual choice above a tab bar that already handles the bottom.

How it works

The provider is a native view, OneNativeSafeAreaProvider, that measures itself and sends its insets and frame to JavaScript whenever they change.

The provider reports only the part of the window insets that overlaps it. A provider that starts below the status bar reports a top inset of 0, so nested layouts never pad twice.

The first frame is already right. Before any view mounts, One reads the window insets synchronously from a native module (initialWindowMetrics) and seeds the root provider with them, so content does not jump when the first measurement arrives.

The root provider also feeds Tamagui’s safe area state, so Tamagui’s safe style values and $safe-area-* variables resolve on iOS and Android and follow rotation.

API reference

One.UI.SafeArea.ViewTypeDescription
edgesEdgesWhich edges to inset: an array of edges, or a record of edge to mode. Default: all four, additive.
EdgesWhich edges to inset: an array of edges, or a record of edge to mode. Default: all four, additive.
mode"padding" | "margin" = "padding"Apply the insets as padding or as margin.
"padding" | "margin" = "padding"Apply the insets as padding or as margin.
childrenReact.ReactNodeThe content.
React.ReactNodeThe content.

The view also takes every View prop. Edges is ('top' | 'right' | 'bottom' | 'left')[], or an object such as { top: 'maximum', bottom: 'additive' } where each edge is one of:

ModeResult
additiveInset plus the style’s own padding or margin
maximumThe larger of the inset and the style’s value
offThe style’s value, unchanged

An edge missing from the array or the record is off.

HooksTypeDescription
useSafeAreaInsets() => EdgeInsetsThe current { top, right, bottom, left } insets of the nearest provider, in points (dp on Android).
() => EdgeInsetsThe current { top, right, bottom, left } insets of the nearest provider, in points (dp on Android).
useSafeAreaFrame() => RectThe nearest provider frame { x, y, width, height }.
() => RectThe nearest provider frame { x, y, width, height }.

One.UI.SafeArea.Provider creates a nested provider, and One.UI.SafeArea.initialMetrics holds the synchronous first reading ({ insets, frame }, or null before the native side can measure).

Platform differences

iOSAndroidWeb
SourceUIView safeAreaInsetsRoot window insetsCSS env() insets
CoversStatus bar, notch, home indicatorStatus, navigation and caption bars, cutoutsWhat the browser reports
Relative toThe provider viewThe provider viewThe window

Limits and gotchas

Examples

Pad a custom header

import { View } from 'react-native'
import { useSafeAreaInsets } from 'one'
function Header({ children }: { children: React.ReactNode }) {
const insets = useSafeAreaInsets()
return <View style={{ paddingTop: insets.top + 8, paddingHorizontal: 16 }}>{children}</View>
}

Adding your own spacing to the inset keeps the gap the same on devices with and without a notch.

Keep a minimum bottom gap

import { One } from 'one'
function Footer({ children }: { children: React.ReactNode }) {
return (
<One.UI.SafeArea.View edges={{ bottom: 'maximum' }} style={{ paddingBottom: 16 }}>
{children}
</One.UI.SafeArea.View>
)
}

With maximum, a phone with a home indicator gets the indicator’s inset, and a phone without one still gets 16.

Edit this page on GitHub.