Conceptos básicos de estilos
10 ejemplos para comenzar con estilos y diseño - 7 básicos y 3 intermedios.
Busca en todas las páginas de la documentación
10 ejemplos para comenzar con estilos y diseño - 7 básicos y 3 intermedios.
Los estilos de React Native se ejecutan en un motor de diseño nativo, no en un navegador. La forma más rápida de probar estos fragmentos es un proyecto de Expo con la plantilla TypeScript predeterminada.
npx create-expo-app@latest MyApp
cd MyApp
npx expo startReemplaza App.tsx con cualquier ejemplo a continuación. react-native-safe-area-context se incluye con Expo - envuelve tu raíz en SafeAreaProvider (la plantilla predeterminada ya lo hace).
Herramientas: Estos ejemplos están dirigidos a Expo SDK 57, React Native 0.86 y React 19.2.3. Las dimensiones están en píxeles independientes de densidad (dp), no
remoem.
Define objetos de estilo reutilizables con StyleSheet.create en lugar de dispersar literales a través de JSX.
import { View, Text, StyleSheet } from "react-native";
export default function App() {
return (
<View style={styles.card}>
<Text style={styles.title}>StyleSheet.create</Text>
<Text style={styles.body}>
Las claves nombradas mantienen los estilos organizados y validados en tiempo de desarrollo.
</Text>
</View>
);
}
const styles = StyleSheet.create({
card: {
margin: 16,
padding: 20,
backgroundColor: "#f0f4ff",
borderRadius: 12,
},
title: { fontSize: 18, fontWeight: "600", marginBottom: 8 },
body: { fontSize: 14, lineHeight: 20, color: "#374151" },
});backgroundColor, borderRadius) - los nombres CSS con guiones no son válidosStyleSheet.create valida claves en desarrollo y registra estilos una sola vez para que el lado nativo reciba IDs establesvh/vwstyles en el scope del módulo para que no se recreen en cada renderizadoRelacionado: Rendimiento de estilos - por qué los estilos cacheados importan en móvil | Mejores prácticas de estilos - nomenclatura y organización de archivos
Los objetos de estilo inline funcionan para casos puntuales, pero se crea una nueva referencia de objeto en cada renderizado a menos que la memoices.
import { View, Text, StyleSheet } from "react-native";
const PADDING = 16;
export default function App() {
const isHighlighted = true;
return (
<View style={styles.container}>
<View style={styles.box}>
<Text>Cacheado vía StyleSheet</Text>
</View>
<View style={{ padding: PADDING, backgroundColor: "#fde68a" }}>
<Text>Literal inline - bien para casos puntuales raros</Text>
</View>
<View
style={[
styles.box,
isHighlighted && { borderWidth: 2, borderColor: "#2563eb" },
]}
>
<Text>Base cacheada + pequeña anulación inline</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 16, gap: 12 },
box: { padding: 16, backgroundColor: "#bbf7d0", borderRadius: 8 },
});style={{ padding: 16 }} asigna un nuevo objeto en cada renderizado - inofensivo para una única prop estática, costoso cuando se pasa profundamente en listasStyleSheet.create para cualquier cosa reutilizada o compuesta en componentesuseMemo son aceptables - perfila antes de optimizarPADDING en el scope del módulo evitan números mágicos sin desencadenar reconstrucciónRelacionado: Rendimiento de estilos -
useMemopara estilos dinámicos y trampas de listas
Cada View es un contenedor flex. El flexDirection predeterminado es column, no row como muchos diseños web asumen.
import { View, Text, StyleSheet } from "react-native";
export default function App() {
return (
<View style={styles.screen}>
<View style={styles.header}>
<Text style={styles.label}>Header (apilado primero)</Text>
</View>
<View style={styles.main}>
<Text style={styles.label}>Contenido principal llena el medio</Text>
</View>
<View style={styles.footer}>
<Text style={styles.label}>Footer (apilado último)</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
screen: { flex: 1, padding: 16 },
header: { padding: 12, backgroundColor: "#dbeafe", borderRadius: 8 },
main: { flex: 1, justifyContent: "center", alignItems: "center", marginVertical: 12 },
footer: { padding: 12, backgroundColor: "#f3f4f6", borderRadius: 8 },
label: { fontWeight: "600" },
});flex: 1 en main hace que consuma el espacio vertical restante entre header y footerjustifyContent alinea a lo largo del eje principal (vertical aquí); alignItems a lo largo del eje cruzado (horizontal)flexDirection: "row" cuando necesites hermanos lado a lado - cubierto en profundidad en la guía flexboxRelacionado: Flexbox en profundidad - dirección, flex grow/shrink y recetas comunes
El espacio en React Native usa el mismo box model que CSS, con props de abreviatura y por borde - todos los números están en dp.
import { View, Text, StyleSheet } from "react-native";
export default function App() {
return (
<View style={styles.screen}>
<View style={styles.card}>
<Text style={styles.title}>Primitivos de espaciado</Text>
<View style={styles.row}>
<View style={styles.chip}>
<Text>A</Text>
</View>
<View style={styles.chip}>
<Text>B</Text>
</View>
<View style={styles.chip}>
<Text>C</Text>
</View>
</View>
</View>
</View>
);
}
const styles = StyleSheet.create({
screen: { flex: 1, padding: 16, backgroundColor: "#f9fafb" },
card: {
padding: 20,
marginBottom: 16,
backgroundColor: "#fff",
borderRadius: 12,
},
title: { marginBottom: 12, fontWeight: "600" },
row: { flexDirection: "row", gap: 8 },
chip: {
paddingHorizontal: 12,
paddingVertical: 8,
backgroundColor: "#e0e7ff",
borderRadius: 16,
},
});padding afecta dentro del elemento; margin empuja lejos de hermanos y bordes del padrepaddingHorizontal / paddingVertical (o marginTop, etc.) cuando los bordes necesitan diferentes valoresgap (RN 0.71+) añade espacio entre hijos flex sin margin en cada hijo - prefierelo en filas y grillasRelacionado: Escala tipográfica y carga de fuentes - altura de línea y espaciado de texto legible | Mejores prácticas de estilos - escalas de espaciado y tokens
Pasa un array a style para capas de base, estado y estilos de anulación - las entradas posteriores ganan en claves conflictivas.
import { useState } from "react";
import { View, Text, Pressable, StyleSheet } from "react-native";
export default function App() {
const [active, setActive] = useState(false);
return (
<View style={styles.container}>
<Pressable onPress={() => setActive((v) => !v)}>
<View style={[styles.tab, active && styles.tabActive]}>
<Text style={[styles.tabText, active && styles.tabTextActive]}>
{active ? "Activo" : "Inactivo"}
</Text>
</View>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: "center", alignItems: "center" },
tab: {
paddingHorizontal: 20,
paddingVertical: 10,
borderRadius: 20,
backgroundColor: "#e5e7eb",
},
tabActive: { backgroundColor: "#2563eb" },
tabText: { color: "#374151", fontWeight: "500" },
tabTextActive: { color: "#fff" },
});style={[styles.base, condition && styles.modifier]} es el patrón estándar para UI impulsada por estadofalse, undefined) se ignoran - no necesitas filtrar el array manualmente[styles.a, styles.b] significa que b anula a en claves duplicadasPressable también acepta un función style ({ pressed }) => [...] para retroalimentación táctil - empareja con arrays para estados complejosRelacionado: Flexbox en profundidad - alineación de layouts compuestos | Modo oscuro y esquemas de color - arrays de estilos conscientes del tema
Lee la preferencia de apariencia del sistema del usuario e intercambia colores de fondo y texto sin una biblioteca de tema separada.
import { View, Text, useColorScheme, StyleSheet } from "react-native";
const palette = {
light: { bg: "#ffffff", text: "#111827", card: "#f3f4f6" },
dark: { bg: "#111827", text: "#f9fafb", card: "#1f2937" },
};
export default function App() {
const scheme = useColorScheme() ?? "light";
const colors = palette[scheme];
return (
<View style={[styles.screen, { backgroundColor: colors.bg }]}>
<View style={[styles.card, { backgroundColor: colors.card }]}>
<Text style={[styles.title, { color: colors.text }]}>
Esquema del sistema: {scheme}
</Text>
<Text style={{ color: colors.text, opacity: 0.7 }}>
Alterna claro/oscuro en la configuración del dispositivo para ver esta actualización.
</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
screen: { flex: 1, padding: 16, justifyContent: "center" },
card: { padding: 20, borderRadius: 12 },
title: { fontSize: 18, fontWeight: "600", marginBottom: 8 },
});useColorScheme() devuelve "light", "dark" o null - establece por defecto "light" cuando es null (algunas configuraciones de Android)palette simple es suficiente para comenzar; extrae tokens a un módulo compartido a medida que la app crece{ color: colors.text }) están bien aquí porque el esquema cambia raramenteRelacionado: Modo oscuro y esquemas de color - paletas dinámicas,
Appearancey sincronización del sistema
Mantén el contenido de la pantalla libre de muescas, barras de estado e indicadores de inicio envolviendo la raíz en SafeAreaView.
import { Text, StyleSheet } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
export default function App() {
return (
<SafeAreaView style={styles.safe}>
<Text style={styles.title}>Layout de área segura</Text>
<Text style={styles.body}>
El contenido respeta los insets del sistema en iOS y edge-to-edge en Android.
</Text>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
safe: { flex: 1, padding: 20, backgroundColor: "#fff" },
title: { fontSize: 22, fontWeight: "bold", marginBottom: 8 },
body: { fontSize: 15, lineHeight: 22, color: "#4b5563" },
});react-native-safe-area-context, no de react-native - el SafeAreaView central es solo para iOS y limitadoSafeAreaProvider - requerido para que se resuelvan los valores de insetflex: 1 en SafeAreaView permite que los hijos llenen el área ajustada por inset, no la pantalla brutauseSafeAreaInsets() cuando solo algunos bordes necesiten padding (p. ej., una imagen de encabezado full-bleed)Relacionado: Áreas seguras y muescas - insets por borde y Android edge-to-edge de RN 0.86
iOS y Android renderizar sombras de manera diferente - Platform.select mantiene un objeto de estilo único con ramas de plataforma.
import { View, Text, Platform, StyleSheet } from "react-native";
export default function App() {
return (
<View style={styles.screen}>
<View style={styles.card}>
<Text style={styles.title}>Tarjeta consciente de plataforma</Text>
<Text style={styles.body}>
iOS usa props de sombra; Android usa elevation.
</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
screen: {
flex: 1,
justifyContent: "center",
padding: 24,
backgroundColor: "#f3f4f6",
},
card: {
backgroundColor: "#fff",
borderRadius: 16,
padding: 20,
...Platform.select({
ios: {
shadowColor: "#000",
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.12,
shadowRadius: 8,
},
android: { elevation: 6 },
}),
},
title: { fontSize: 18, fontWeight: "600", marginBottom: 8 },
body: { fontSize: 14, color: "#6b7280", lineHeight: 20 },
});Platform.select devuelve el valor coincidente para el SO actual - expándelo en objetos StyleSheet de manera limpiashadowColor, shadowOffset, shadowOpacity, shadowRadius juntos; Android usa elevationPlatform.OS funciona para ramas JSX pequeñas; prefiere Platform.select cuando solo valores de estilo difierenRelacionado: Sombras, elevación y bordes - líneas de cabello, overflow y límites de elevación
Deriva valores de layout del ancho de ventana actual para que la rotación y tablets refluyan sin anchos de píxeles codificados.
import { View, Text, useWindowDimensions, StyleSheet } from "react-native";
export default function App() {
const { width } = useWindowDimensions();
const columns = width >= 768 ? 3 : 2;
const gap = 12;
const horizontalPadding = 32;
const tileSize =
(width - horizontalPadding - gap * (columns - 1)) / columns;
return (
<View style={styles.container}>
<Text style={styles.heading}>
{columns} columnas - {Math.round(width)}px de ancho
</Text>
<View style={[styles.grid, { gap }]}>
{Array.from({ length: 6 }, (_, i) => (
<View
key={i}
style={[styles.tile, { width: tileSize, height: tileSize }]}
>
<Text>Loseta {i + 1}</Text>
</View>
))}
</View>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 16 },
heading: { fontSize: 16, fontWeight: "600", marginBottom: 16 },
grid: { flexDirection: "row", flexWrap: "wrap" },
tile: {
backgroundColor: "#e0e7ff",
borderRadius: 8,
justifyContent: "center",
alignItems: "center",
},
});useWindowDimensions re-renderiza en rotación y redimensionamiento - a diferencia de la captura única de Dimensions.get("window")width menos padding y gaps - las dimensiones numéricas evitan sorpresas porcentuales en grillas flexTABLET = 768) compartidas en pantallas para comportamiento adaptativo consistenteRelacionado: Layout receptivo y adaptativo - tablets, plegables y vista dividida
Combina StyleSheet, useColorScheme y un pequeño módulo de token en un patrón reutilizable que las apps de producción usan antes de recurrir a una biblioteca de estilos.
import { View, Text, useColorScheme, StyleSheet } from "react-native";
const spacing = { sm: 8, md: 16, lg: 24 } as const;
const themes = {
light: {
screen: "#f9fafb",
card: "#ffffff",
text: "#111827",
muted: "#6b7280",
border: "#e5e7eb",
},
dark: {
screen: "#111827",
card: "#1f2937",
text: "#f9fafb",
muted: "#9ca3af",
border: "#374151",
},
} as const;
export default function App() {
const scheme = useColorScheme() ?? "light";
const theme = themes[scheme];
return (
<View style={[styles.screen, { backgroundColor: theme.screen }]}>
<View
style={[
styles.card,
{ backgroundColor: theme.card, borderColor: theme.border },
]}
>
<Text style={[styles.label, { color: theme.muted }]}>Cuenta</Text>
<Text style={[styles.value, { color: theme.text }]}>alex@example.com</Text>
<View style={[styles.divider, { backgroundColor: theme.border }]} />
<Text style={[styles.label, { color: theme.muted }]}>Plan</Text>
<Text style={[styles.value, { color: theme.text }]}>Pro - se renueva el 1 de ago</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
screen: { flex: 1, justifyContent: "center", padding: spacing.md },
card: {
borderRadius: 12,
borderWidth: 1,
padding: spacing.lg,
},
label: { fontSize: 13, marginBottom: spacing.sm },
value: { fontSize: 16, fontWeight: "600", marginBottom: spacing.md },
divider: { height: StyleSheet.hairlineWidth, marginBottom: spacing.md },
});spacing, themes) viven al lado o encima de componentes - layout estático en StyleSheet, colores semánticos del objeto temaStyleSheet.hairlineWidth es la línea visible más delgada en el dispositivo actual - úsalo para divisores en lugar de codificar 1as const para que las claves permanezcan estrechas y los errores de tipografía fallen en tiempo de compilacióntheme.ts y alinéalos con tu sistema de diseño - consulta las mejores prácticas antes de adoptar styled-components o NativeWindRelacionado: Mejores prácticas de estilos - nomenclatura de tokens e intercambios de biblioteca | Modo oscuro y esquemas de color - sincronización de barra de navegación y barra de estado con tema
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