expo-dev-client convierte tu binario de depuración en un entorno de desarrollo apropiado: un lanzador para cambiar entre servidores Metro, un menú de desarrollo extensible, y soporte para cada módulo nativo que tu proyecto instala - la ruta estándar más allá de Expo Go en SDK 57.
Tarjeta de referencia rápida - lista para copiar y pegar.
# Añade la librería (fija la versión compatible para SDK 57)npx expo install expo-dev-client# Compila e instala un cliente de desarrollonpx expo run:ios # simulador local / dispositivo USBnpx expo run:android --device# - o en la nube -eas build --profile development --platform all# Sirve JavaScript al cliente de desarrollo (no Expo Go)npx expo start --dev-client
// app.json - comportamiento del lanzador (reconstrucción requerida después de cambios){ "expo": { "plugins": [ [ "expo-dev-client", { "launchMode": "most-recent", "ios": { "launchMode": "launcher" } } ] ] }}
Cuándo usarlo:
Instalar cualquier módulo nativo no incluido en Expo Go (react-native-maps con configuración personalizada, Stripe, etc.).
QA necesita cambiar entre URLs de Metro de localhost, staging y vista previa de PR.
Añadir entradas personalizadas al menú de desarrollo de agitar / tres dedos.
Probar enlaces universales, notificaciones push e iconos de aplicación con tu identificador de paquete real.
Mantenerse en un flujo de trabajo gestionado mientras se supera la superficie nativa fija de Expo Go.
// App.tsx - elementos personalizados del menú de desarrollo + banner de tiempo de ejecuciónimport { useEffect } from "react";import { Button, Platform, StyleSheet, Text, View } from "react-native";import * as DevClient from "expo-dev-client";import * as Expo from "expo";async function registerDevTools() { if (!__DEV__) return; await DevClient.registerDevMenuItems([ { name: "Registra configuración de aplicación", callback: () => { console.log("¿Ejecutándose en Expo Go?", Expo.isRunningInExpoGo()); }, shouldCollapse: true, }, { name: "Lanza error de prueba", callback: () => { throw new Error("Intención del menú de desarrollo fallo para QA"); }, shouldCollapse: true, }, ]);}export default function App() { useEffect(() => { registerDevTools().catch(console.error); }, []); return ( <View style={styles.container}> <Text style={styles.title}>demo de expo-dev-client</Text> <Text style={styles.row}>Plataforma: {Platform.OS}</Text> <Text style={styles.row}> Ejecutándose en Expo Go: {Expo.isRunningInExpoGo() ? "sí" : "no"} </Text> <Text style={styles.hint}> Agita el dispositivo (o presiona m en la terminal) - abre el menú de desarrollo - prueba los elementos personalizados. </Text> <Button title="Abre menú de desarrollo" onPress={() => DevClient.openMenu()} /> </View> );}const styles = StyleSheet.create({ container: { flex: 1, justifyContent: "center", alignItems: "center", padding: 24 }, title: { fontSize: 22, fontWeight: "700", marginBottom: 12 }, row: { fontSize: 14, color: "#475569", marginBottom: 4 }, hint: { marginTop: 16, fontSize: 12, color: "#64748b", textAlign: "center" },});
// eas.json - perfil de desarrollo con cliente de desarrollo habilitado{ "cli": { "version": ">= 16.0.0" }, "build": { "development": { "developmentClient": true, "distribution": "internal", "ios": { "simulator": true }, "android": { "gradleCommand": ":app:assembleDebug" } } }}
# Flujo de primera vez después de añadir expo-dev-clientnpx expo prebuild # omite si android/ e ios/ ya existeneas build --profile development --platform ios# Instala compilación - luego diariamente:npx expo start --dev-client
Lo que esto demuestra:
registerDevMenuItems para diagnósticos específicos del equipo sin editar código nativo.
DevClient.openMenu() como alternativa programática a los gestos de agitar.
eas.jsondevelopmentClient: true para que EAS produzca un binario de depuración con el lanzador integrado.
Bandera --dev-client para que Metro nunca asuma que Expo Go es el anfitrión.
expo-dev-client es un módulo nativo más un plugin de configuración que conecta una interfaz de lanzador, esquema de enlace profundo y menú de desarrollo mejorado en compilaciones de depuración.
Development builds (binarios de depuración con expo-dev-client) cargan JavaScript desde Metro - la misma historia de Fast Refresh que Expo Go, pero el lado nativo es tu id de aplicación, iconos y módulos instalados.
launchMode controla el inicio en frío: most-recent salta a la última URL del proyecto; launcher muestra el selector de servidor (útil para que QA cambie entre entornos).
defaultLaunchURL omite completamente el selector cuando se establece - empareja con anulaciones por plataforma para emulador Android (10.0.2.2) vs simulador iOS (localhost).
Los cambios nativos (nuevo plugin de configuración, expo prebuild, ediciones de Gradle/Pod) requieren npx expo run:* o eas build; los cambios puros de JS/TS hacen hot-reload a través de npx expo start --dev-client.
Añade filas personalizadas con name, callback, shouldCollapse opcional
Los elementos personalizados se ejecutan en el hilo JS - mantén los callbacks rápidos; difiere el trabajo pesado con queueMicrotask o InteractionManager.
# 1. Añade una dependencia nativanpx expo install expo-camera# 2. Regenera proyectos nativos si usas CNGnpx expo prebuild# 3. Recompila el cliente de desarrollo (cambio nativo)npx expo run:android --device# 4. Itera en JS - sin reconstrucción necesaria hasta que cambie el código nativo nuevamentenpx expo start --dev-client
Protege todas las APIs de dev-client con __DEV__ para que las compilaciones de lanzamiento de producción desactiven las llamadas (las APIs son sin operaciones en lanzamiento, pero proteger mantiene los paquetes limpios).
Iniciar Metro sin --dev-client - La CLI puede esperar a Expo Go o mostrar un QR incorrecto. Solución: Siempre npx expo start --dev-client una vez que el proyecto dependa de expo-dev-client.
Editar la configuración del plugin app.json sin reconstruir - La configuración del lanzador es de tiempo de compilación. Solución: Ejecuta npx expo run:* o eas build después de cambios de plugin.
El emulador de Android no puede alcanzar localhost:8081 - El emulador mapea el loopback del anfitrión a 10.0.2.2. Solución: Establece android.defaultLaunchURL a http://10.0.2.2:8081 o usa --lan.
registerDevMenuItems después de hot reload duplica entradas - Los elementos se añaden por registro. Solución: Registra una sola vez en una protección if (__DEV__ && !globalThis.__DEV_MENU__) a nivel de módulo.
Esperar que Expo Go cargue módulos nativos personalizados - Expo Go incluye un binario fijo. Solución: Instala tu compilación de desarrollo; Native module cannot be null significa que necesitas una reconstrucción.
Usar compilaciones de desarrollo para pruebas de rendimiento de tienda - Los clientes dev de depuración incluyen herramientas adicionales. Solución: Perfila en compilaciones preview o de lanzamiento para métricas de FPS e inicio.
Confusión de Expo Go en simulador de iOS - El simulador aún puede tener Expo Go instalado junto a tu cliente de desarrollo. Solución: Abre el icono de aplicación correcto (el nombre de tu aplicación, no "Expo Go").
Una librería y plugin de configuración que añade un lanzador de desarrollo, menú de desarrollo mejorado y herramientas de depuración a tu propio binario de aplicación de depuración - el nombre de Expo para una "compilación de desarrollo".
¿Cómo instalo expo-dev-client en SDK 57?
npx expo install expo-dev-client
Nunca fijes la versión manualmente - expo install resuelve el lanzamiento compatible con SDK 57.
¿Por qué debo ejecutar expo start --dev-client?
La bandera le dice a Expo CLI que genere URLs y códigos QR para tu binario personalizado. Sin ella, las herramientas asumen que Expo Go es el anfitrión de destino.
¿Qué es launchMode most-recent vs launcher?
most-recent abre la última URL de Metro directamente (vuelve a launcher si no es accesible). launcher siempre muestra el selector de servidor - mejor para que QA cambie entre ramas.
¿Cuándo necesito reconstruir la aplicación nativa?
Después de añadir/eliminar módulos nativos, cambiar plugins de configuración, editar iconos/splash/id de paquete de app.json, o actualizar Expo SDK. Los edits puros de JS hacen hot-reload sin reconstrucción.
¿Cómo añado botones personalizados del menú de desarrollo?
import * as DevClient from "expo-dev-client";await DevClient.registerDevMenuItems([ { name: "Mi acción", callback: () => console.log("ejecuta"), shouldCollapse: true },]);
Registra una sola vez al iniciar la aplicación en __DEV__ solamente.
¿Cómo abro el menú de desarrollo sin agitar el teléfono?
Presiona m en la terminal de Metro, llama DevClient.openMenu() desde JS, o usa la pulsación larga de tres dedos en simuladores de iOS.
¿Puedo usar expo-dev-client con Expo Router?
Sí - la plantilla predeterminada de SDK 57 incluye Router. El cliente de desarrollo carga tu entrada de router (app/_layout.tsx) como cualquier otra aplicación.
¿Qué perfil de eas.json construye un cliente de desarrollo?
Establece "developmentClient": true en un perfil de compilación (comúnmente nombrado development). Ejecuta eas build --profile development.
¿Funciona expo-dev-client en simuladores?
Sí. Usa "ios": { "simulator": true } en el perfil de desarrollo para IPAs de simulador, o npx expo run:ios localmente.
¿Qué URL deben usar los emuladores de Android para Metro?
http://10.0.2.2:8081 - el alias del emulador para el localhost de la máquina anfitriona. Establece android.defaultLaunchURL en la configuración del plugin.
¿Se incluye expo-dev-client en las compilaciones de producción?
El shell nativo es el mismo proyecto, pero la interfaz de dev-client se elimina de las compilaciones de lanzamiento. Envía eas build --profile production para binarios de tienda.
¿Cómo es esto diferente de Expo Go?
Expo Go es una aplicación compartida con un conjunto fijo de módulos nativos. Un cliente de desarrollo es tu binario de aplicación con tus dependencias nativas e identificador de paquete.
Trampa: Native module cannot be null - ¿y ahora qué?
Instala expo-dev-client, reconstruye con npx expo run:android o eas build --profile development, instala el nuevo binario, luego inicia Metro con --dev-client.