Sombras, Elevación & Bordes
Diferencias de plataforma entre sombras iOS y elevación de Android.
Busca en todas las páginas de la documentación
Diferencias de plataforma entre sombras iOS y elevación de Android.
Tarjeta de referencia rápida - lista para copiar y pegar.
import { Platform, StyleSheet, Text, View } from "react-native";
export function ElevatedCard({ title, children }: { title: string; children: React.ReactNode }) {
return (
<View style={styles.shadowWrapper}>
<View style={styles.card}>
<Text style={styles.title}>{title}</Text>
{children}
</View>
</View>
);
}
const styles = StyleSheet.create({
shadowWrapper: {
...Platform.select({
ios: {
shadowColor: "#0f172a",
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.12,
shadowRadius: 12,
},
android: { elevation: 4 },
}),
},
card: {
backgroundColor: "#fff",
borderRadius: 16,
padding: 16,
gap: 8,
borderWidth: StyleSheet.hairlineWidth,
borderColor: "#e2e8f0",
overflow: "hidden",
},
title: { fontSize: 17, fontWeight: "600", color: "#0f172a" },
});Cuándo usarlo: Tarjetas, hojas, botones flotantes o filas de lista que necesiten profundidad - quieres un componente que se vea correcto en iOS y Android sin pelear contra sombras recortadas o elevación faltante.
import { Image, Platform, Pressable, StyleSheet, Text, View } from "react-native";
const AVATAR = require("./assets/avatar.png");
export function ProfileHeader() {
return (
<View style={styles.screen}>
<View style={styles.heroShadow}>
<View style={styles.heroCard}>
<View style={styles.avatarRing}>
<Image source={AVATAR} style={styles.avatar} />
</View>
<Text style={styles.name}>Alex Chen</Text>
<Text style={styles.subtitle}>React Native - Expo SDK 57</Text>
</View>
</View>
<Pressable style={({ pressed }) => [styles.fabShadow, pressed && styles.fabPressed]}>
<View style={styles.fab}>
<Text style={styles.fabLabel}>+</Text>
</View>
</Pressable>
<View style={styles.dividerRow}>
<View style={styles.hairline} />
<Text style={styles.dividerLabel}>Reciente</Text>
<View style={styles.hairline} />
</View>
</View>
);
}
const styles = StyleSheet.create({
screen: { flex: 1, padding: 20, backgroundColor: "#f8fafc", gap: 24 },
heroShadow: {
borderRadius: 20,
...Platform.select({
ios: {
shadowColor: "#000",
shadowOffset: { width: 0, height: 6 },
shadowOpacity: 0.1,
shadowRadius: 16,
},
android: { elevation: 6 },
}),
},
heroCard: {
backgroundColor: "#fff",
borderRadius: 20,
padding: 20,
alignItems: "center",
gap: 8,
borderWidth: StyleSheet.hairlineWidth,
borderColor: "#cbd5e1",
},
avatarRing: {
width: 88,
height: 88,
borderRadius: 44,
borderWidth: 3,
borderColor: "#2563eb",
overflow: "hidden",
...Platform.select({
ios: { borderCurve: "continuous" },
default: {},
}),
},
avatar: { width: "100%", height: "100%" },
name: { fontSize: 22, fontWeight: "700", color: "#0f172a" },
subtitle: { fontSize: 14, color: "#64748b" },
fabShadow: {
alignSelf: "flex-end",
borderRadius: 28,
...Platform.select({
ios: {
shadowColor: "#2563eb",
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.35,
shadowRadius: 8,
},
android: { elevation: 8 },
}),
},
fabPressed: { opacity: 0.9, transform: [{ scale: 0.96 }] },
fab: {
width: 56,
height: 56,
borderRadius: 28,
backgroundColor: "#2563eb",
alignItems: "center",
justifyContent: "center",
},
fabLabel: { fontSize: 28, color: "#fff", lineHeight: 32 },
dividerRow: { flexDirection: "row", alignItems: "center", gap: 12 },
hairline: { flex: 1, height: StyleSheet.hairlineWidth, backgroundColor: "#cbd5e1" },
dividerLabel: { fontSize: 12, fontWeight: "600", color: "#94a3b8", letterSpacing: 1 },
});Lo que demuestra esto:
shadowWrapper externo - la tarjeta interna puede usar overflow: "hidden" sin recortar la sombra iOSPlatform.select empareja props shadow* de iOS con elevation de Android en un único StyleSheetStyleSheet.hairlineWidth para bordes y líneas de divisor que permanecen nítidas en todas las densidadesborderCurve: "continuous" en iOS para avatares circulares más suave (ignorado en Android)elevation en Android para un efecto flotanteiOS renderiza sombras del fondo y canal alfa de la vista. Cuatro props controlan el efecto:
| Prop | Rol | Valor de inicio típico |
|---|---|---|
shadowColor | Matiz de sombra | "#000" o color de marca |
shadowOffset | Desplazamiento { width, height } en dp | { width: 0, height: 4 } |
shadowOpacity | Alfa 0-1 | 0.08 - 0.15 para tarjetas |
shadowRadius | Radio de desenfoque en dp | 8 - 16 para tarjetas suaves |
// Bloque de sombra solo iOS
{
shadowColor: "#0f172a",
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 8,
}Requisitos en iOS:
backgroundColor opaco o translúcido en la vista sombreada - los fondos transparentes producen sombras débiles o invisibles.overflow: "hidden" corta la sombra - aplica propiedades de sombra a un wrapper padre; mantén overflow: "hidden" solo en la vista de contenido interna.borderRadius perfectamente en todas las versiones de OS - el wrapper + radio interno generalmente coincide con la intención de diseño.Android usa un único número elevation (dp) para dibujar una sombra de material y colocar la vista por encima de hermanos.
elevation | Peso visual | Uso típico |
|---|---|---|
0 | Plano | Filas de lista alineadas |
2 - 4 | Levantamiento sutil | Tarjetas, chips |
6 - 8 | Prominente | FABs, modales |
12+ | Pesado | Diálogos, hojas inferiores |
// Elevación solo Android
{ elevation: 4, backgroundColor: "#fff" }Requisitos en Android:
backgroundColor es obligatorio - la elevación se dibuja desde la forma de fondo de la vista.shadowColor, shadowOffset, shadowOpacity y shadowRadius son ignorados.const cardShadow = Platform.select({
ios: {
shadowColor: "#000",
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.12,
shadowRadius: 12,
},
android: { elevation: 4 },
default: {},
});
// Dispersar en entrada de StyleSheet
card: {
backgroundColor: "#fff",
borderRadius: 12,
...cardShadow,
},Para tokens de sistema de diseño, exporta presets de sombra:
export const shadows = {
sm: Platform.select({
ios: { shadowColor: "#000", shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.06, shadowRadius: 4 },
android: { elevation: 2 },
}),
md: Platform.select({
ios: { shadowColor: "#000", shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.1, shadowRadius: 10 },
android: { elevation: 4 },
}),
} as const;| Prop | Notas |
|---|---|
borderWidth | Uniforme o por lado vía borderTopWidth, etc. |
borderColor | Se aplica a todos los lados a menos que se anule por lado |
borderRadius | Redondea esquinas; usa props por esquina para radios asimétricos |
borderStyle | "solid" (predeterminado), "dotted", "dashed" - el soporte de dashed varía |
StyleSheet.hairlineWidth | Línea más delgada visible (-1 píxel físico) |
// Borde por lado (común para separadores de lista)
{
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: "#e2e8f0",
}
// Máscara redondeada para imágenes
{
borderRadius: 12,
overflow: "hidden",
}Solo iOS: borderCurve: "continuous" produce esquinas más suaves en radios grandes - especialmente avatares y píldoras. Android ignora esta prop de forma segura.
- shadowWrapper (sombra / elevación, borderRadius) -
| - card (backgroundColor, overflow: hidden) - |
| | contenido |
| - -
- -
Usa un wrapper cuando la vista interna necesita overflow: "hidden" para imágenes, degradados o elementos secundarios recortados pero aún debe proyectar una sombra en iOS.
import type { ViewStyle } from "react-native";
import { Platform, StyleSheet } from "react-native";
type ShadowPreset = Pick<
ViewStyle,
"shadowColor" | "shadowOffset" | "shadowOpacity" | "shadowRadius" | "elevation"
>;
const presets: Record<"sm" | "md", ShadowPreset | undefined> = {
sm: Platform.select({ ios: { shadowColor: "#000", shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.06, shadowRadius: 4 }, android: { elevation: 2 } }),
md: Platform.select({ ios: { shadowColor: "#000", shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.1, shadowRadius: 10 }, android: { elevation: 4 } }),
};Platform.select es una unión - dispersar en StyleSheet.create es válido.shadowOffset requiere { width: number; height: number } - no una tupla.borderCurve es específico de iOS; TypeScript puede incluirlo en ViewStyle detrás de extensiones de plataforma.Sombra invisible en iOS - Falta backgroundColor o fondo de tarjeta completamente transparente. Solución: Establece backgroundColor: "#fff" (o tu color de superficie) en la vista sombreada.
overflow: "hidden" corta sombra - Común en tarjetas con imágenes redondeadas. Solución: Mueve propiedades de sombra a un wrapper externo; mantén overflow: "hidden" solo en la vista interna.
Props de sombra en Android - shadowColor y amigos hacen silenciosamente nada. Solución: Siempre empareja con elevation vía Platform.select.
Elevación sin fondo - Android no dibuja nada. Solución: backgroundColor explícito en cada superficie elevada.
Bordes hairline demasiado gruesos - Usar borderWidth: 1 en pantallas retina se ve pesado. Solución: StyleSheet.hairlineWidth para separadores y contornos de tarjeta.
Bordes discontinuos inconsistentes - borderStyle: "dashed" se renderiza diferente en plataformas. Solución: Usa SVG o un componente de borde dedicado para dashes perfectos en píxeles.
Sombra en Text - El Text central no soporta props de sombra de forma confiable para profundidad de estilo tarjeta. Solución: Envuelve texto en una View que lleve la sombra/elevación.
Elevaciones compitiendo en pilas - Múltiples hermanos elevados pueden producir sombras Android turbias. Solución: Eleva solo el elemento flotante superior; mantén filas de lista planas (elevation: 0).
| Alternativa | Usarlo cuando | No usarlo cuando |
|---|---|---|
Presets de sombra Platform.select | Tarjetas estándar, FABs, chips | Necesitas mockups de sombra idénticas perfectas en píxeles en ambos OS |
| Wrapper + tarjeta interna | Contenido redondeado con overflow: "hidden" | Chips planos simples sin recorte |
react-native-shadow-2 (comunidad) | Sombras de caja de múltiples capas complejas desde diseño | Aplicaciones Expo gestionadas evitando deps nativos adicionales |
Border falso expo-linear-gradient | Bordes de degradado sin SVG | borderColor sólido simple es suficiente |
box-shadow vía WebView / Skia | Diseño requiere sombras CSS exactas | Tarjetas nativas normales y listas |
| Solo bordes planos (sin sombra) | Tablas de datos densas y listas de configuración | Tarjetas hero de marketing que necesiten profundidad |
backgroundColor - las sombras se renderan desde la capa de fondo.overflow: "hidden" en la misma vista corta la propagación de sombra.shadowOpacity: 0 o un shadowRadius cero hace el efecto invisible.backgroundColor, mueve la sombra a un wrapper padre, y comienza con shadowOpacity: 0.1 y shadowRadius: 8.elevation - un único número en píxeles independientes de densidad.backgroundColor en la misma vista.2 y 6; FABs entre 6 y 12.elevation.Platform.select por nivel (sm, md, lg) mantienen los equipos consistentes.overflow: "hidden" corta todo fuera de la caja de borde, incluyendo el desenfoque de sombra.View externo lleva la sombra; View interno tiene borderRadius, backgroundColor y overflow: "hidden".borderWidth: 1 para separadores de lista y contornos de tarjeta sutiles.borderWidth y para height en divider Views.borderRadius.elevation hacia abajo si se ve demasiado pesada.borderCurve: "continuous" junto con borderRadius en iOS.elevation en cada fila multiplica las llamadas de dibujo de sombra.Text soporta textShadowColor, textShadowOffset y textShadowRadius para contornos de glifo.shadow* son para contenedores, no profundidad de párrafo.Text en una View elevada.{
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: "#e2e8f0",
}borderTopWidth, borderLeftColor, etc.borderWidth solo establece los cuatro lados.shadowOpacity en iOS - las sombras negras fuertes se ven duras.backgroundColor distinto.Animated y Reanimated pueden interpolar shadowOpacity y elevation para estados de presión.opacity + scale en lugar de animar el radio de desenfoque.outline.borderWidth / borderColor en enfoque, o una View de anillo de enfoque dedicada.shadows.sm, shadows.md, shadows.lg como objetos Platform.select.StyleSheet.create para evitar desviación de copiar-pegar.backgroundColor de superficie de tu tema.StyleSheet.create y el modelo mental de estilo RNPlatform.select y primitivas de layout de tarjeta.ios / .androidoverflow: "hidden"Versiones de Stack: Esta página fue escrita 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: 16 jul 2026