Creación de tu Primera App con Expo CLI

Video
20 min~13 min lectura
Objetivo de la lección

Introducción: Más Allá del "Hello World" La creación de tu primera aplicación con Expo CLI es un rito de iniciación en el desarrollo móvil con React Native.

Puntos de control
  • Introducción: Más Allá del "Hello World"
  • Configuración del Entorno y Creación del Proyecto Base
  • Concepto Clave: El Flujo de Trabajo "Managed" de Expo
  • Cómo Funciona en la Práctica: Anatomía de un Proyecto Expo y Tu Primer Cambio

Reproductor de video

Introducción: Más Allá del "Hello World"

La creación de tu primera aplicación con Expo CLI es un rito de iniciación en el desarrollo móvil con React Native. Sin embargo, esta lección no se trata simplemente de replicar un tutorial básico. Como desarrollador intermedio, tu objetivo debe ser comprender la arquitectura que se está construyendo, las decisiones que toma Expo por ti y las herramientas que tienes a tu disposición desde el primer comando. Expo CLI no es solo un generador de proyectos; es un entorno de desarrollo completo que abstrae la complejidad nativa (iOS/Android) y proporciona un flujo de trabajo unificado.

En esta lección, desglosaremos cada paso, desde la inicialización del proyecto hasta la ejecución en múltiples dispositivos, explicando el "por qué" detrás de cada archivo generado y cada comando ejecutado. Abordaremos configuraciones recomendadas para proyectos serios, cómo estructurar tu código desde el día uno para facilitar el escalado, y cómo el ecosistema Expo (Go, Dev Client, EAS) se integra en este proceso inicial. Vamos a construir una aplicación funcional con navegación y estado, no un simple texto estático.

Configuración del Entorno y Creación del Proyecto Base

Antes de ejecutar el primer comando, es crucial verificar que tu entorno cumple con los requisitos. Para un nivel intermedio, esto implica más que tener Node.js instalado. Debes asegurarte de tener una versión LTS (Long-Term Support) de Node (por ejemplo, la 18.x o 20.x) para garantizar compatibilidad con las últimas versiones de Expo y React Native. Además, aunque Expo maneja las SDK nativas, para un desarrollo óptimo y la capacidad de depurar problemas complejos, es recomendable tener instalados los entornos de desarrollo nativos (Xcode para macOS y Android Studio para cualquier SO). Esto te permitirá, en el futuro, ejecutar tu proyecto directamente en simuladores nativos o ejectar a un flujo de trabajo bare si es necesario.

La creación del proyecto se realiza con el comando npx create-expo-app. La elección de la plantilla (template) es tu primera decisión arquitectónica. Para una aplicación que planeas llevar a producción, evita la plantilla en blanco absoluto ("blank"). En su lugar, utiliza plantillas como "tabs" o "bare-minimum" que ofrecen una estructura más organizada. Especificar un TypeScript template desde el inicio ahorrará una migración posterior. También es el momento de decidir si tu proyecto será managed (el flujo estándar de Expo) o si necesitas desde el inicio un proyecto con acceso al código nativo (bare workflow), aunque para la mayoría de los casos, el managed workflow es más que suficiente y recomendado.


# Verificar versiones (prerrequisito)
node --version
npm --version

# Crear un nuevo proyecto con TypeScript y una plantilla de pestañas
npx create-expo-app MiPrimeraAppExpo --template tabs@latest

# Navegar al directorio del proyecto
cd MiPrimeraAppExpo

# Iniciar el servidor de desarrollo de Expo
npx expo start

Al ejecutar npx expo start, se inicia el servidor de desarrollo de Expo (también conocido como Metro Bundler) y se abre una interfaz en tu navegador (la Expo Dev UI) y/o aparece un código QR en la terminal. Este es el centro de control de tu desarrollo. Desde aquí puedes elegir ejecutar la aplicación en un simulador iOS (con la tecla 'i'), un emulador Android (con la tecla 'a'), o en tu dispositivo físico escaneando el QR con la app Expo Go. La generación del proyecto crea una estructura de archivos clave: app.json (o expo.json) para la configuración, package.json con las dependencias, y un directorio app/ o src/ dependiendo de la plantilla, que utiliza React Navigation y un patrón basado en archivos para la navegación.

Concepto Clave: El Flujo de Trabajo "Managed" de Expo

Imagina que quieres construir una casa. El flujo de trabajo managed de Expo es como contratar a un contratista general (Expo) que se encarga de todos los cimientos, la fontanería, la electricidad y la estructura (las configuraciones nativas de iOS y Android). Tú, el desarrollador, te concentras únicamente en el diseño de interiores, la decoración y la funcionalidad de las habitaciones (el código JavaScript/TypeScript y los componentes React Native). Expo te proporciona una "casa" ya habitable con servicios básicos instalados, sobre la cual tú construyes.

En términos técnicos, Expo actúa como una capa de abstracción sobre los proyectos nativos de Xcode y Android Studio. Gestiona automáticamente el archivo de configuración (app.json), las dependencias nativas compatibles (a través de la SDK de Expo), y el proceso de construcción. Esto elimina la necesidad de tocar código Objective-C, Swift, Java o Kotlin para la gran mayoría de las funcionalidades comunes (cámara, geolocalización, notificaciones push, etc.). El servidor de desarrollo (Metro) empaqueta tu código JavaScript sobre la marcha y lo sirve a la aplicación Expo Go en tu dispositivo, permitiendo una recarga en caliente (Hot Reloading) instantánea.

Tip Profesional: El managed workflow es ideal para el 80-90% de las aplicaciones. Solo considera el bare workflow si necesitas una biblioteca nativa que no es compatible con Expo o si requieres un control extremadamente fino sobre el código nativo. Incluso entonces, herramientas como EAS Build y Development Builds (Dev Client) permiten mantener gran parte de la conveniencia de Expo.

Cómo Funciona en la Práctica: Anatomía de un Proyecto Expo y Tu Primer Cambio

Una vez creado el proyecto, es vital entender lo que hay dentro. Abre el proyecto en tu editor de código. El archivo app.json es el corazón de la configuración: define el nombre, slug, versiones, orientación, icono adaptativo y los plugins de Expo que extienden la funcionalidad. El package.json lista las dependencias; nota que "expo" es una dependencia clave que incluye la SDK, y "react-native" es manejada por Expo. La carpeta assets contiene tus recursos estáticos (imágenes, fuentes).

La plantilla "tabs" genera una estructura dentro de la carpeta app que utiliza el enrutador de archivos de Expo. Verás archivos como (tabs), _layout.tsx, y index.tsx. Este sistema, basado en React Navigation, mapea la estructura de archivos a rutas de navegación. Tu primer cambio significativo será modificar la pantalla de inicio. Navega a app/(tabs)/index.tsx. Aquí no solo cambiarás un texto, sino que añadirás un estado simple y un botón interactivo, conectando la interfaz de usuario con la lógica.


// app/(tabs)/index.tsx - Pantalla de Inicio Modificada
import { StyleSheet, View, Text, Pressable } from 'react-native';
import { useState } from 'react';

export default function HomeScreen() {
  // Estado para manejar un contador
  const [count, setCount] = useState(0);

  // Función para incrementar el contador
  const handleIncrement = () => {
    setCount(prevCount => prevCount + 1);
  };

  // Función para reiniciar el contador
  const handleReset = () => {
    setCount(0);
  };

  return (
    
      ¡Bienvenido a Mi App Expo!
      Esta es tu primera aplicación interactiva.
      
      
        Contador de Clics:
        {count}
        
        
           [
              styles.button,
              styles.buttonIncrement,
              pressed && styles.buttonPressed
            ]} 
           
          >
            Incrementar
          
          
           [
              styles.button,
              styles.buttonReset,
              pressed && styles.buttonPressed
            ]} 
           
          >
            Reiniciar
          
        
      
      
      Usa los tabs inferiores para navegar.
    
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    padding: 20,
    backgroundColor: '#f5f5f5',
  },
  title: {
    fontSize: 28,
    fontWeight: 'bold',
    marginBottom: 10,
    color: '#333',
    textAlign: 'center',
  },
  subtitle: {
    fontSize: 16,
    color: '#666',
    marginBottom: 40,
    textAlign: 'center',
  },
  counterCard: {
    backgroundColor: 'white',
    borderRadius: 16,
    padding: 30,
    alignItems: 'center',
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 8,
    elevation: 5, // Para Android
    width: '100%',
    maxWidth: 350,
  },
  counterLabel: {
    fontSize: 18,
    color: '#555',
    marginBottom: 10,
  },
  counterValue: {
    fontSize: 72,
    fontWeight: '700',
    color: '#007AFF', // Azul iOS
    marginBottom: 30,
  },
  buttonContainer: {
    flexDirection: 'row',
    gap: 15,
  },
  button: {
    paddingVertical: 12,
    paddingHorizontal: 24,
    borderRadius: 10,
    minWidth: 120,
    alignItems: 'center',
    justifyContent: 'center',
  },
  buttonIncrement: {
    backgroundColor: '#007AFF',
  },
  buttonReset: {
    backgroundColor: '#FF3B30',
  },
  buttonPressed: {
    opacity: 0.8,
    transform: [{ scale: 0.98 }],
  },
  buttonText: {
    color: 'white',
    fontSize: 16,
    fontWeight: '600',
  },
  footer: {
    marginTop: 40,
    fontSize: 14,
    color: '#999',
    fontStyle: 'italic',
  },
});

Al guardar este archivo, Metro Bundler detectará el cambio y recargará la aplicación automáticamente en tu dispositivo o simulador (Fast Refresh). Verás cómo la pantalla de inicio se transforma de un texto estático a una aplicación interactiva con estado, estilos y retroalimentación táctil. Este es el ciclo de desarrollo central: editar código, guardar, y ver los cambios al instante. Ahora, explora el archivo app/(tabs)/_layout.tsx para entender cómo se configuran las pestañas de navegación y modifica los iconos o nombres de las otras pantallas en (tabs)/explore.tsx y (tabs)/settings.tsx.

Código en Acción: Integrando un Módulo de Expo (Camera) en una Nueva Pantalla

Para demostrar el poder del ecosistema Expo, vamos a crear una nueva pantalla fuera del tab navigator que utilice un módulo nativo: la Cámara. Esto ilustrará cómo añadir funcionalidades nativas complejas con una API JavaScript sencilla. Primero, necesitamos instalar el módulo de la cámara de la SDK de Expo. Luego, crearemos una nueva pantalla y la conectaremos a la navegación.

Iniciamos instalando el paquete: npx expo install expo-camera. Este comando es crucial porque asegura que se instale la versión compatible con la SDK de Expo que tu proyecto usa. Luego, crea un nuevo archivo en la carpeta app llamado camera.tsx. Este archivo se convertirá automáticamente en una nueva ruta accesible (/camera). Implementaremos una vista de cámara con permisos y un botón para tomar fotos.


// app/camera.tsx - Pantalla de Cámara
import { useState, useEffect, useRef } from 'react';
import { StyleSheet, Text, View, Image, Alert } from 'react-native';
import { CameraView, CameraType, useCameraPermissions } from 'expo-camera';
import { StatusBar } from 'expo-status-bar';
import { Link } from 'expo-router';
import { Pressable } from 'react-native';

export default function CameraScreen() {
  const [facing, setFacing] = useState('back');
  const [permission, requestPermission] = useCameraPermissions();
  const [photo, setPhoto] = useState(null);
  const cameraRef = useRef(null);

  // Efecto para solicitar permisos al montar
  useEffect(() => {
    if (!permission) {
      requestPermission();
    }
  }, [permission]);

  // Función para alternar entre cámara frontal y trasera
  const toggleCameraFacing = () => {
    setFacing(current => (current === 'back' ? 'front' : 'back'));
  };

  // Función para tomar una foto
  const takePicture = async () => {
    if (cameraRef.current) {
      try {
        const takenPhoto = await cameraRef.current.takePictureAsync({
          quality: 0.8,
          base64: false,
        });
        if (takenPhoto?.uri) {
          setPhoto(takenPhoto.uri);
          Alert.alert('¡Éxito!', 'Foto capturada correctamente.');
        }
      } catch (error) {
        Alert.alert('Error', 'No se pudo tomar la foto: ' + (error as Error).message);
      }
    }
  };

  // Estado de permisos
  if (!permission) {
    return (
      
        Solicitando permisos...
      
    );
  }

  if (!permission.granted) {
    return (
      
        Necesitamos tu permiso para usar la cámara
        
          Conceder Permiso
        
        Volver al Inicio
      
    );
  }

  return (
    
      
      
      {!photo ? (
        // Vista de la cámara activa
        
          
            
              
                🔄
              
              
                
              
               {/* Para alinear */}
            
          
        
      ) : (
        // Vista de previsualización de la foto
        
          
          
             setPhoto(null)}>
              Tomar Otra
            
             Alert.alert('Guardar', 'Funcionalidad de guardado a implementar.')}
            >
              Guardar Foto
            
          
        
      )}
      
      Volver al Inicio
    
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#000',
  },
  permissionText: {
    color: 'white',
    fontSize: 18,
    textAlign: 'center',
    marginBottom: 20,
  },
  permissionButton: {
    backgroundColor: '#007AFF',
    paddingHorizontal: 20,
    paddingVertical: 12,
    borderRadius: 10,
  },
  permissionButtonText: {
    color: 'white',
    fontSize: 16,
    fontWeight: '600',
  },
  camera: {
    flex: 1,
  },
  cameraOverlay: {
    flex: 1,
    backgroundColor: 'transparent',
    justifyContent: 'flex-end',
    paddingBottom: 40,
  },
  cameraControls: {
    flexDirection: 'row',
    justifyContent: 'space-around',
    alignItems: 'center',
  },
  flipButton: {
    backgroundColor: 'rgba(0,0,0,0.5)',
    width: 60,
    height: 60,
    borderRadius: 30,
    justifyContent: 'center',
    alignItems: 'center',
  },
  buttonIcon: {
    fontSize: 24,
  },
  captureButton: {
    backgroundColor: 'rgba(255,255,255,0.3)',
    width: 80,
    height: 80,
    borderRadius: 40,
    justifyContent: 'center',
    alignItems: 'center',
    borderWidth: 4,
    borderColor: 'white',
  },
  captureButtonInner: {
    backgroundColor: 'white',
    width: 64,
    height: 64,
    borderRadius: 32,
  },
  placeholder: {
    width: 60,
  },
  previewContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 20,
  },
  previewImage: {
    width: '100%',
    height: '70%',
    borderRadius: 20,
    resizeMode: 'cover',
  },
  previewControls: {
    flexDirection: 'row',
    marginTop: 30,
    gap: 20,
  },
  actionButton: {
    paddingHorizontal: 25,
    paddingVertical: 15,
    borderRadius: 12,
    minWidth: 140,
    alignItems: 'center',
  },
  retakeButton: {
    backgroundColor: '#FF3B30',
  },
  saveButton: {
    backgroundColor: '#34C759',
  },
  actionButtonText: {
    color: 'white',
    fontSize: 16,
    fontWeight: '600',
  },
  backLink: {
    position: 'absolute',
    top: 60,
    left: 20,
    backgroundColor: 'rgba(0,0,0,0.7)',
    paddingHorizontal: 15,
    paddingVertical: 10,
    borderRadius: 20,
    zIndex: 10,
  },
  backLinkText: {
    color: 'white',
    fontSize: 16,
  },
});

Para acceder a esta pantalla, podemos añadir un botón en nuestra pantalla de inicio (app/(tabs)/index.tsx) que utilice el componente Link de Expo Router. Añade este botón dentro del `View` contenedor, cerca del final: <Link href="/camera" style={ { marginTop: 20, padding: 10, backgroundColor: '#5856D6', borderRadius: 10 } }><Text style={ { color: 'white' } }>Abrir Cámara</Text></Link>. Ahora, al hacer clic en este botón, navegarás a la pantalla de la cámara. Este ejemplo integra permisos, uso de referencias (ref), estado complejo y una API nativa, todo dentro del managed workflow de Expo.

Errores Comunes y Cómo Evitarlos

Al comenzar con Expo CLI, incluso desarrolladores intermedios pueden tropezar con ciertos problemas. Aquí hay cinco errores frecuentes y sus soluciones:

1. Error: "SDK version XX.X.X not supported": Esto ocurre cuando una dependencia que instalas (especialmente con `npm install` en lugar de `npx expo install`) requiere una versión diferente de la SDK de Expo. Solución: Siempre usa `npx expo install <paquete>` para paquetes relacionados con Expo o React Native. Este comando elige la versión compatible automáticamente. Si el error persiste, verifica la versión de la SDK en tu `app.json` y la compatibilidad del paquete.

2. La aplicación en Expo Go se cierra o se pone en blanco tras un cambio: Suele deberse a un error de JavaScript no capturado durante el Fast Refresh. Solución: Revisa la terminal donde ejecutas `expo start`. Metro mostrará el error de compilación (sintaxis, módulo no encontrado). También puedes abrir el menú de desarrollo en la app (agitar el dispositivo) y habilitar "Debug Remote JS" para ver los errores en la consola del navegador.

3. No se ven los cambios en el dispositivo físico (QR escaneado): A menudo es un problema de red. Tu teléfono y tu computadora deben estar en la misma red Wi-Fi. Solución: Verifica la conectividad. En la Dev UI (navegador), puedes cambiar la conexión a "Tunnel" en lugar de "LAN". Esto crea un túnel a través de internet, útil en redes corporativas restrictivas, pero es más lento.

4. Problemas con estilos o dimensiones en diferentes dispositivos: Asumir tamaños de pantalla fijos (píxeles absolutos) lleva a UI rotas. Solución: Adopta desde el inicio un diseño responsivo. Usa `flex`, porcentajes, `Dimensions.get('window')` para cálculos, y la API `useWindowDimensions` de React Native. Para espaciado consistente, define una constante base y usa multiplicadores.

5. Confusión entre Expo Go y Development Build (Dev Client): Intentar usar un módulo nativo que no está incluido en Expo Go (como ciertas bibliotecas de mapas o pagos) causará un error. Solución: Comprende la diferencia. Expo Go es una app genérica con un conjunto preinstalado de módulos. Para usar bibliotecas nativas personalizadas, debes crear un Development Build personalizado usando EAS Build. Planifica esto desde el inicio si tu app requiere funcionalidades fuera del conjunto estándar de Expo Go.

Checklist de Dominio

Al completar esta lección, debes ser capaz de verificar los siguientes puntos. Si puedes marcar todos, has asimilado correctamente los fundamentos de creación de apps con Expo CLI a un nivel intermedio.

  • Puedo crear un nuevo proyecto Expo usando una plantilla específica (Tabs, TypeScript) desde la terminal y justificar mi elección.
  • Comprendo la diferencia entre el managed y bare workflow de Expo y sé cuándo elegir cada uno.
  • Puedo navegar y explicar la función de los archivos clave generados: app.json, package.json, y la estructura del directorio app/ con Expo Router.
  • Sé iniciar el servidor de desarrollo, ejecutar la app en un simulador iOS/Android y en un dispositivo físico escaneando el QR, y solucionar problemas básicos de conectividad.
  • He modificado una pantalla existente añadiendo estado interactivo (useState), estilos complejos (StyleSheet) y componentes de Pressable con feedback.
  • He integrado exitosamente un módulo nativo de la SDK de Expo (como expo-camera) en una nueva pantalla, manejando permisos y usando su API.
  • Puedo explic
De lección a portfolio

Convertí esta lección en una prueba técnica visible.

Una app pequeña publicada, con README y decisiones explicadas, funciona mejor que una lista de tecnologías sueltas.

Paso 1

Creá una demo mínima que use el concepto de la lección.

Paso 2

Escribí un README corto con objetivo, stack, decisión técnica y mejora futura.

Paso 3

Publicá la demo y enlazala desde tu perfil profesional.

Newsletter Cursalo

Recibí rutas y cursos nuevos

Sumate para recibir recursos orientados a empleo y portfolio.

  • Rutas de empleo
  • Cursos prácticos
  • Portfolio y entrevistas

Sin spam. También podés entrar con tu cuenta para guardar progreso. Iniciá sesión

Creación de tu Primera App con Expo CLI | CursaloFalar no WhatsApp