Áreas seguras y notches
react-native-safe-area-context y Android edge-to-edge en RN 0.86.
Busca en todas las páginas de la documentación
react-native-safe-area-context y Android edge-to-edge en RN 0.86.
Tarjeta de receta de referencia rápida - lista para copiar y pegar.
import { StyleSheet, Text, View } from "react-native";
import { SafeAreaProvider, SafeAreaView } from "react-native-safe-area-context";
export function AppRoot({ children }: { children: React.ReactNode }) {
return <SafeAreaProvider>{children}</SafeAreaProvider>;
}
export function Screen({ title, children }: { title: string; children: React.ReactNode }) {
return (
<SafeAreaView style={styles.safe} edges={["top", "left", "right"]}>
<View style={styles.header}>
<Text style={styles.title}>{title}</Text>
</View>
<View style={styles.body}>{children}</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
safe: { flex: 1, backgroundColor: "#fff" },
header: { paddingHorizontal: 16, paddingBottom: 8 },
title: { fontSize: 28, fontWeight: "700" },
body: { flex: 1, paddingHorizontal: 16 },
});Cuándo usarlo: Cualquier layout de pantalla completa en iPhones con notch, dispositivos Dynamic Island, o Android 15+ edge-to-edge donde las barras del sistema se superponen a tu contenido.
import { ScrollView, StatusBar, StyleSheet, Text, View } from "react-native";
import {
SafeAreaProvider,
SafeAreaView,
useSafeAreaInsets,
} from "react-native-safe-area-context";
function ArticleScreen() {
const insets = useSafeAreaInsets();
return (
<View style={styles.screen}>
<StatusBar barStyle="dark-content" translucent backgroundColor="transparent" />
{/* Hero edge-to-edge - inset top manual para texto legible bajo la barra de estado */}
<View style={[styles.hero, { paddingTop: insets.top + 12 }]}>
<Text style={styles.heroTitle}>Áreas seguras</Text>
<Text style={styles.heroSubtitle}>Edge-to-edge con insets legibles</Text>
</View>
{/* Contenido estándar - SafeAreaView maneja los lados; inset bottom para indicador de inicio */}
<SafeAreaView style={styles.content} edges={["left", "right", "bottom"]}>
<ScrollView contentContainerStyle={styles.scroll}>
<Text style={styles.paragraph}>
En RN 0.86, Android se dibuja detrás de las barras de estado y navegación. iOS ha hecho esto durante años. react-native-safe-area-context reporta el rectángulo sin obstrucciones para que tu UI permanezca táctil.
</Text>
</ScrollView>
</SafeAreaView>
</View>
);
}
export default function App() {
return (
<SafeAreaProvider>
<ArticleScreen />
</SafeAreaProvider>
);
}
const styles = StyleSheet.create({
screen: { flex: 1, backgroundColor: "#0f172a" },
hero: {
paddingHorizontal: 20,
paddingBottom: 24,
backgroundColor: "#1e293b",
},
heroTitle: { fontSize: 32, fontWeight: "800", color: "#f8fafc" },
heroSubtitle: { fontSize: 16, color: "#94a3b8", marginTop: 4 },
content: { flex: 1, backgroundColor: "#fff", borderTopLeftRadius: 16, borderTopRightRadius: 16 },
scroll: { padding: 20 },
paragraph: { fontSize: 16, lineHeight: 24, color: "#334155" },
});Lo que esto demuestra:
SafeAreaProvider en la raíz de la app para que los valores de inset se propaguen a todas las pantallas.useSafeAreaInsets() para un hero que se extiende edge-to-edge con padding top manual.SafeAreaView con edges selectivos - hero maneja top; el contenido maneja bottom y lados.StatusBar configurado en translucent para que Android edge-to-edge se dibuje detrás de la barra de estado.react-native-safe-area-context lee los valores de inset nativos y los expone a través de React context. Es la librería recomendada - incluida en plantillas de Expo y plantillas de comunidad RN.SafeAreaProvider debe envolver tu app (o cada raíz modal) para que los hooks y SafeAreaView puedan leer los insets actuales. La root layout de Expo Router típicamente incluye esto.SafeAreaView aplica padding igual al inset en cada lado seleccionado. Es un envoltorio de conveniencia - funcionalmente equivalente a paddingTop: insets.top, etc.useSafeAreaInsets() devuelve { top, right, bottom, left } en píxeles independientes de densidad. Úsalo cuando necesites control fino (botones flotantes, headers personalizados, padding parcial).edges - un array como ["top", "left", "right"] que omite bottom cuando una barra de pestañas o barra de herramientas del teclado ya maneja ese inset.React Native 0.86 (Expo SDK 57) dibuja el contenido de la app detrás de las barras de estado y navegación del sistema en Android por defecto - coincidiendo con la guía moderna Android 15+ edge-to-edge.
| Preocupación | Antes de edge-to-edge | RN 0.86 edge-to-edge |
|---|---|---|
| Superposición de barra de estado | El sistema reservó una barra sólida | El contenido se dibuja detrás; añade inset top |
| Barra de navegación | Barra de navegación opaca | Gesto/barra de navegación superpuesta al contenido; añade inset bottom |
| Color de fondo | Establecido solo mediante StatusBar | Extiende el fondo a sangre; aplica padding al contenido |
| Teclado | Comportamiento estándar | Sigue usando KeyboardAvoidingView - separado del área segura |
import { Platform, StatusBar } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
function EdgeToEdgeShell({ children }: { children: React.ReactNode }) {
const insets = useSafeAreaInsets();
return (
<View
style={{
flex: 1,
paddingTop: Platform.OS === "android" ? insets.top : 0, // SafeAreaView a menudo maneja iOS
paddingBottom: insets.bottom,
}}
>
<StatusBar translucent backgroundColor="transparent" barStyle="dark-content" />
{children}
</View>
);
}Para configuración edge-to-edge a nivel de Gradle y expo-navigation-bar, ver Edge-to-Edge en Android.
| API | Mejor para | Evita cuando |
|---|---|---|
SafeAreaView | Envoltorios de pantalla completa, pantallas simples | Necesitas different padding por sub-región (usa hook) |
useSafeAreaInsets() | Headers personalizados, FABs, bottom sheets, mapas | Copiarías los mismos cuatro paddings en todas partes (usa SafeAreaView) |
initialWindowMetrics | Handoff splash-to-app sin parpadeo de inset | Pantallas normales en-app (el provider maneja actualizaciones) |
import { useSafeAreaInsets } from "react-native-safe-area-context";
function FloatingActionButton({ onPress }: { onPress: () => void }) {
const insets = useSafeAreaInsets();
return (
<Pressable
onPress={onPress}
style={{
position: "absolute",
right: 16,
bottom: insets.bottom + 16, // limpia indicador de inicio / barra de navegación
}}
/>
);
}| Tipo de pantalla | edges recomendado | Por qué |
|---|---|---|
| Pantalla de stack (sin pestañas) | ["top", "left", "right", "bottom"] | Inset completo en todos los lados |
| Contenido de pantalla de pestañas | ["top", "left", "right"] | La barra de pestañas maneja inset bottom |
| Modal / bottom sheet | ["bottom"] o ["top", "bottom"] | Depende del estilo de presentación |
| Hero inmersivo + body | Hook en hero; SafeAreaView en body | División de propiedad de inset |
import type { EdgeInsets } from "react-native-safe-area-context";
import type { Edge } from "react-native-safe-area-context";
const TAB_EDGES: Edge[] = ["top", "left", "right"];
function useBottomInset(extra = 0): number {
const { bottom } = useSafeAreaInsets();
return bottom + extra;
}
// Pasa insets a una utilidad no-React
function contentHeight(windowHeight: number, insets: EdgeInsets): number {
return windowHeight - insets.top - insets.bottom;
}Edge es "top" | "right" | "bottom" | "left".Usar SafeAreaView del núcleo desde react-native - El componente integrado es deprecado y solo maneja notches de iOS de manera confiable. Fix: Importa desde react-native-safe-area-context.
Falta SafeAreaProvider - useSafeAreaInsets() devuelve ceros; el contenido se sienta bajo la barra de estado. Fix: Envuelve la raíz de la app (la plantilla de Expo hace esto; verifica después de expulsar o entrada personalizada).
Double bottom padding encima de barras de pestañas - edges={["bottom"]} en una pantalla de pestañas añade padding y la barra de pestañas añade su propio área segura. Fix: Omite "bottom" de edges cuando el navegador de pestañas posee ese inset.
Double top padding en SafeAreaViews anidados - Anidar dos envoltorios ambos con edges={["top"]} duplica el inset. Fix: Un propietario de área segura por eje por pantalla.
Android edge-to-edge sin padding de inset - RN 0.86 se dibuja detrás de las barras del sistema; el texto y los botones se vuelven intacables bajo la barra de navegación. Fix: Aplica padding de useSafeAreaInsets() o SafeAreaView al contenido, no solo el color de la barra de estado.
Ignorar insets de notch en horizontal - Los insets left y right son distintos de cero en iPhones con notch en horizontal. Fix: Incluye "left" y "right" en edges, o usa el hook para padding horizontal.
Área segura vs teclado - El inset bottom no se mueve cuando se abre el teclado. Fix: Combina área segura con KeyboardAvoidingView o react-native-keyboard-controller para entradas de texto.
| Alternativa | Usa cuando | No uses cuando |
|---|---|---|
react-native-safe-area-context | Defecto para todas las apps de producción | Nunca - esta es la solución estándar |
Hook useSafeAreaInsets | Headers personalizados, FABs, mapas, padding parcial | Un envoltorio de pantalla completa simple es suficiente (SafeAreaView) |
Manual paddingTop: 44 | Nunca en producción | Siempre - los valores de inset varían por dispositivo y orientación |
KeyboardAvoidingView | Entradas de texto cerca del fondo de la pantalla | Reemplazando área segura - resuelven problemas diferentes |
expo-status-bar + expo-navigation-bar | Controlando estilo/color de barra en Android edge-to-edge | Midiendo insets - aún necesitas safe-area-context |
El SafeAreaView del núcleo solo cuenta para áreas seguras de iOS y es deprecado. react-native-safe-area-context funciona en iOS y Android, soporta la prop edges, y expone useSafeAreaInsets para control granular.
Una sola vez en la raíz de la app - típicamente en App.tsx, root _layout.tsx de Expo Router, o tu contenedor de navegación. Cada pantalla y modal que lee insets debe ser descendiente del provider.
Lista qué lados reciben padding automático de inset. edges={["top", "left", "right"]} salta bottom - común en pantallas de pestañas donde la barra de pestañas ya limpia el indicador de inicio. Default (omitido) aplica los cuatro lados.
El contenido ahora se dibuja detrás de las barras de estado y navegación. Debes añadir padding de inset top y bottom (vía SafeAreaView o useSafeAreaInsets) para que el texto y los botones permanezcan visibles y táctiles. Los colores de fondo pueden extenderse a sangre detrás de las barras.
SafeAreaView - envoltorio rápido de pantalla completa.useSafeAreaInsets - layouts personalizados (hero edge-to-edge, botón de acción flotante, superposiciones de mapa).Producen los mismos valores de inset; elige basado en la complejidad del layout.
Probablemente aplicaste edges={["bottom"]} en una pantalla dentro de un navegador de pestañas que ya cuenta para el indicador de inicio. Elimina "bottom" de edges en pantallas raíz de pestañas.
Envuelve contenido modal en su propio SafeAreaProvider solo si el modal renderiza fuera del árbol del provider principal (raro). Usualmente, envuelve contenido modal en SafeAreaView con edges apropiados para la presentación - bottom sheets necesitan inset bottom; modales de pantalla completa necesitan todos los lados.
Sí. Horizontal en iPhones con notch mueve inset de top a left o right. useSafeAreaInsets re-renderiza automáticamente con valores actualizados - no cachees insets en useRef a través de cambios de orientación.
No envuelvas la imagen en SafeAreaView. En su lugar, extiende la imagen a top: 0 y añade paddingTop: insets.top en el texto superpuesto a la imagen para que las etiquetas permanezcan legibles.
Sí. SafeAreaView es una View con padding. Usa style={{ flex: 1 }} como de costumbre. El padding reduce el área de contenido interior - tus hijos flex hacen layout dentro de la caja ajustada de inset.
import { StatusBar } from "expo-status-bar";
<StatusBar style="auto" translucent />Empareja barStyle con tu fondo - texto claro en hero oscuro, texto oscuro en body blanco. Ver Edge-to-Edge en Android para tema de barra de navegación.
SafeAreaProvider acepta initialWindowMetrics para hidratar insets correctos en el primer frame - reduciendo un salto de layout visible en arranque frío. Las plantillas de Expo a menudo configuran esto; añádelo si ves un destello de contenido bajo la barra de estado al iniciar.
useWindowDimensions devuelve el tamaño de ventana completo. Los insets de área segura describen cuánto de esa ventana está obstruido. Altura de contenido utilizable ≈ height - insets.top - insets.bottom. Ver Dimensiones & Responsive Layout.
Pon padding consciente de inset en el contentContainerStyle del ScrollView o envuelve el área de scroll en SafeAreaView. Para un header flotante fuera del scroll, aplica insets.top solo en el header.
No - useSafeAreaInsets().bottom permanece igual cuando se abre el teclado. Usa KeyboardAvoidingView o una librería de controlador de teclado para desplazar campos de entrada encima del teclado por separado.
flex: 1 dentro de envoltorios de área segurabarStyle de barra de estado por esquema de colorexpo-navigation-baruseWindowDimensions junto con insetsVersiones de Stack: Esta página se escribió para React 19.2.3, React Native 0.86.0, y Expo SDK 57 (
expo~57.0.4).
Revisado por Chris St. John·Última actualización: 19 jul 2026