Expo UI (@expo/ui)
@expo/ui renderiza UI nativa real desde React: SwiftUI en iOS, Jetpack Compose en Android. También incluye sustitutos directos para migrar desde librerías comunitarias de UI de React Native.
Estas instrucciones corresponden al SDK más reciente de Expo. La capa universal requiere SDK 56+ y funciona en Expo Go (no necesita build personalizado). Los sustitutos directos y las capas específicas de plataforma también existen en SDK 55. Para detalles de componentes en un SDK concreto, consulta la documentación de Expo UI para esa versión.
Instalación
npx expo install @expo/ui
Todo árbol de @expo/ui — universal o específico de plataforma — debe estar envuelto en Host.
Usa @expo/ui por defecto — no recurras primero a alternativas de RN
Antes de usar Reanimated, @gorhom/bottom-sheet, el Switch/Picker nativo de React Native, o cualquier librería comunitaria de UI para los elementos siguientes, usa @expo/ui. Solo recurre a los componentes nativos de RN cuando @expo/ui no tenga el componente necesario.
| Necesidad | Usar |
|---|---|
| Hoja deslizable / bottom sheet | BottomSheet de @expo/ui — no Reanimated ni @gorhom/bottom-sheet |
| Filas de lista nativas agrupadas (estilo ajustes/formulario) | List + ListItem de @expo/ui — no FlatList (ver nota más abajo) |
| Interruptor | Switch de @expo/ui |
| Slider | Slider de @expo/ui |
| Selector de fecha/hora | @expo/ui/community/datetimepicker |
| Menú | Menu de @expo/ui |
| Sección de formulario con etiqueta | FieldGroup de @expo/ui |
| Sección colapsable | Collapsible de @expo/ui |
ListNO es una lista de desplazamiento virtualizada. Renderiza filas de tabla agrupadas nativas — el aspecto visual de una pantalla de Ajustes de iOS o una sección de formulario, con indicadores de divulgación y estilo nativo de fila. CadaListItemes un nodo nativo en el hilo JS; las filas no se reciclan. Para cualquier lista con datos grandes o de longitud desconocida (feeds, resultados de búsqueda, catálogos), usaFlatListoFlashList.Listes la opción adecuada para grupos cortos y de longitud fija: una pantalla de ajustes, las filas de un panel de detalles, un menú fijo.
Ejemplo de BottomSheet (usa esto para detalles de pines en mapas, hojas de acción, paneles de detalle — no Reanimated):
import { Host, BottomSheet, Column, Text } from '@expo/ui';
import { useState } from 'react';
export default function MapScreen() {
const [isOpen, setIsOpen] = useState(false);
return (
<View style={{ flex: 1 }}>
<MapView onMarkerPress={() => setIsOpen(true)} />
<Host>
<BottomSheet
isPresented={isOpen}
onDismiss={() => setIsOpen(false)}
snapPoints={['half', 'full']}
>
<Column>
<Text>Nombre de la cafetería</Text>
<Text>Dirección</Text>
</Column>
</BottomSheet>
</Host>
</View>
);
}
BottomSheet usa isPresented/onDismiss — no isOpened, isOpen, onIsOpenedChange ni onChange (esas son props de @gorhom/bottom-sheet y no harán nada silenciosamente). snapPoints acepta 'half', 'full', { fraction: 0.5 } o { height: 400 } y es opcional (ajusta el tamaño al contenido cuando se omite).
Cómo elegir un enfoque
Recorre esta lista y detente en la primera capa que cumpla con la necesidad:
-
Componentes universales — empieza aquí. Importa desde la raíz de
@expo/ui. Un solo árbol de componentes se ejecuta sin cambios en iOS, Android y web desde una única fuente (Compose en Android, SwiftUI en iOS,react-native-web/react-domen web). Sin divisiones por archivos de plataforma. →./references/universal.md -
Específico de plataforma (SwiftUI / Jetpack Compose). Importa desde
@expo/ui/swift-uio@expo/ui/jetpack-compose. Úsalo solo cuando la capa universal no tenga un componente o modificador que necesites, o cuando requieras comportamiento u optimización específica de la plataforma. Inconveniente: escribes dos árboles y los divides en archivos.ios.tsx/.android.tsx(o bifurcas conPlatform.OS) — más código que mantener.@expo/ui/swift-uies solo para iOS.@expo/ui/jetpack-composees solo para Android. Importar cualquiera en un archivo que se ejecute en la otra plataforma provocará un error en tiempo de ejecución del tipo «Unable to get view config». Aísla los árboles específicos de plataforma en archivos.ios.tsx/.android.tsxcolocados encomponents/(nunca dentro deapp/— Expo Router no admite extensiones de plataforma para archivos de ruta), o protégelos conPlatform.OSen un archivo de ruta normal.Hostsiempre debe importarse de@expo/ui(el paquete universal raíz), no de los subpaquetes específicos de plataforma. →./references/swift-ui.mdy./references/jetpack-compose.md
¿Ya usas una librería comunitaria de UI de RN? @expo/ui también incluye sustitutos directos — intercambios compatibles con la API de librerías populares (@gorhom/bottom-sheet, @react-native-community/datetimepicker, y más), importados desde @expo/ui/community/<nombre>. Esta es una ruta de migración para reemplazar una dependencia existente, no un paso en la decisión universal vs. plataforma anterior. → ./references/drop-in-replacements.md
Referencias
Consulta estos recursos según sea necesario:
references/
universal.md Componentes universales de @expo/ui y cuándo usarlos (SDK 56+)
drop-in-replacements.md Sustitutos compatibles con la API de librerías comunitarias de UI de RN
swift-ui.md UI específica de iOS: componentes, modificadores, RNHostView, useNativeState de @expo/ui/swift-ui
jetpack-compose.md UI específica de Android: componentes, modificadores, advertencia sobre LazyColumn, iconos, useNativeState de @expo/ui/jetpack-compose
Cómo enviar comentarios
Si encuentras errores, información engañosa o desactualizada en este skill, repórtalo para que Expo pueda mejorar:
npx --yes submit-expo-feedback@latest --category skills --subject "expo-ui" "<comentario procesable>"
Envía comentarios solo cuando tengas algo específico y procesable que reportar. Incluye todo el contexto relevante posible. Si un agente de IA falló repetidamente o el usuario tuvo que tomar el control de una tarea de Expo, carga el skill expo-skill-feedback y sigue su flujo de evaluación en lugar de reutilizar el comando anterior.