Saltar al contenido principal
Tutorial

Cómo crear apps móviles con Angular y Capacitor

Aprende a crear una app móvil con Angular, Capacitor, y mejora la navegación nativa, transiciones y buenas prácticas de diseño en iOS de Capgo.

Crear apps móviles con Angular y Capacitor Capgo ilustración de blog

En este tutorial, empezaremos con una nueva Angular la app y pasar al mundo de las apps móviles nativas usando Capacitor. También puedes añadir Capgo Native Navigation and Transitions para una sensación nativa, y usar tailwind-capacitor para safe areas.

Capacitor te permite convertir fácilmente tu web app de Angular en una app móvil nativa sin necesidad de grandes cambios o aprender algo nuevo como React Native.

Con solo unos pocos pasos sencillos, la mayoría de las aplicaciones Angular pueden convertirse en apps móviles.

Este tutorial te guiará por el proceso, empezando con una nueva app de Angular y luego integrando Capacitor para adentrarte en el mundo de las apps móviles nativas. También puedes usar Capgo Native Navigation, Transitions y tailwind-capacitor para las áreas seguras.

Acerca de Capacitor

¡CapacitorJS lo cambia todo! Puedes incorporarlo sin esfuerzo en cualquier proyecto web y empaquetará tu aplicación en una webview nativa, generando por ti los proyectos de Xcode y Android Studio. Además, sus plugins dan acceso a funciones nativas del dispositivo, como la cámara, mediante un bridge de JS.

Con Capacitor, obtienes una app móvil nativa fantástica sin configuraciones complicadas ni una curva de aprendizaje pronunciada. Su ligero API y su funcionalidad optimizada hacen que integrarlo en tu proyecto sea pan comido. ¡Créeme, te sorprenderá lo fácil que es conseguir una app nativa totalmente funcional con Capacitor!

Preparando tu app de Angular

Para crear una nueva app de Angular, ejecuta el siguiente comando:

ng new my-app
cd my-app

Elige “Angular” cuando se te pida la versión de Angular.

Para crear una app móvil nativa, necesitamos un Exportar de nuestro proyecto. Así que añadamos un script sencillo en nuestro package.json que puedes usar para compilar y copiar el proyecto de Angular:

{
  "scripts": {
    // ...
    "build": "ng build --prod"
  }
}

Tras ejecutar el comando build, deberías poder detectar un nuevo dist carpeta en la raíz de tu proyecto.

Esta carpeta la usará Capacitor más adelante, pero por ahora debemos configurarla correctamente.

Cómo añadir Capacitor a tu app de Angular

Para empaquetar cualquier web app en un contenedor móvil nativo, hay que seguir unos pasos iniciales, pero después basta con ejecutar un solo sync comando.

Primero, podemos instalar el Capacitor CLI como dependencia de desarrollo y luego configurarlo en nuestro proyecto. Durante la configuración, puedes pulsar “enter” para aceptar los valores por defecto de name y bundle ID.

A continuación, necesitamos instalar el core package y los paquetes necesarios para las plataformas iOS y Android.

Por fin, podemos añadir las plataformas y Capacitor creará carpetas para cada una en la raíz del proyecto:

# Install the Capacitor CLI locally
npm install -D @capacitor/cli

# Initialize Capacitor in your Angular project
npx cap init

# Install the required packages
npm install @capacitor/core @capacitor/ios @capacitor/android

# Add the native platforms
npx cap add ios
npx cap add android

A estas alturas, ya deberías poder ver nuevas ios y Android carpetas en tu proyecto de Angular.

¡Son proyectos nativos reales!

Para acceder al proyecto de Android más tarde, debes instalar Android Studio. Para iOS, necesitas un Mac y debes instalar Xcode.

Además, deberías encontrar un capacitor.config.ts archivo en tu proyecto, que contiene algunos ajustes fundamentales de Capacitor que se usan durante la sincronización. Lo único a lo que debes prestar atención es al webDir, que debe apuntar al resultado de tu comando de build. Actualmente, es incorrecto.

Para corregirlo, abre el archivo capacitor.config.json y actualiza el webDir:

{
  "appId": "com.example.app",
  "appName": "my-app",
  "webDir": "dist"
}

Puedes probarlo ejecutando los siguientes comandos:

npm run build
npx cap sync

El primer comando npm run build simplemente hará el build de tu proyecto Angular y copiará el build estático, mientras que el segundo comando npx cap sync sincronizará todos los code web en los lugares correctos de las plataformas nativas para que se muestren en una app.

Además, el comando sync podría actualizar las plataformas nativas e instalar plugins, así que cuando instales un nuevo plugins de Capacitor es hora de ejecutar npx cap sync de nuevo.

¡Sin darte cuenta, ya has terminado, así que veamos la app en un dispositivo!

Build y deploy de apps nativas

Para desarrollar apps de iOS, necesitas tener Xcode instalado, y para apps de Android, necesitas tener Android Studio instalados. Además, si planeas distribuir tu app en la App Store, tienes que inscribirte en el Apple Developer Program para iOS y en Google Play Console para Android.

Si eres nuevo en el desarrollo móvil nativo, puedes usar el Capacitor CLI para abrir ambos proyectos nativos fácilmente:

npx cap open ios
npx cap open android

Una vez configurados tus proyectos nativos, desplegar tu app en un dispositivo conectado es fácil. En Android Studio, solo debes esperar a que todo esté listo y podrás desplegar tu app sin cambiar ningún ajuste. Aquí tienes un ejemplo:

ejecutar-android-studio

En Xcode, debes configurar tu cuenta de firma para desplegar tu app en un dispositivo real en lugar de solo el simulador. Si no lo has hecho antes, Xcode te guía por el proceso (pero, de nuevo, debes estar inscrito en el Developer Program). Después, solo tienes que pulsar play para ejecutar la app en tu dispositivo conectado, que puedes seleccionar en la parte superior. Aquí tienes un ejemplo:

ejecutar-xcode

¡Felicidades! Has desplegado con éxito tu app web de Angular en un dispositivo móvil. Aquí tienes un ejemplo:

app-móvil-angular

Pero espera, también hay una forma más rápida de hacer esto durante el desarrollo…

Live Reload con Capacitor

A estas alturas, probablemente estés acostumbrado al hot reload de todos los frameworks modernos, y la buena noticia es que puedes tener la misma funcionalidad en un dispositivo móvil ¡con el mínimo esfuerzo!

Habilita el acceso a tu aplicación alojada localmente con live reload on your network haciendo que la app Capacitor cargue el contenido desde la URL específica.

El primer paso es averiguar tu IP local. Si usas Mac, puedes encontrarla ejecutando este comando en la terminal:

ipconfig getifaddr en0

En Windows, ejecuta:

ipconfig

Luego busca la dirección IPv4.

Podemos indicar a Capacitor que cargue la app directamente desde el servidor añadiendo otra entrada a nuestro capacitor.config.ts archivo:

import { CapacitorConfig } from '@capacitor/cli';

const config: CapacitorConfig = {
  appId: 'com.example.app',
  appName: 'my-app',
  webDir: 'dist',
  bundledWebRuntime: false,
  server: {
    url: 'http://192.168.x.xx:4200',
    cleartext: true
  }
};

export default config;

Asegúrate de usar la IP y el puerto correctos, en este ejemplo he usado el puerto por defecto de Angular.

Ahora podemos aplicar estos cambios copiándolos a nuestro proyecto nativo:

npx cap copy

El copy el comando es similar a sync, pero solo podrá copiará los cambios realizados en la carpeta web y la configuración, sin actualizar el proyecto nativo.

Ahora puedes desplegar tu app una vez más mediante Android Studio o Xcode. Después de eso, si cambias algo en tu app de Angular, la app se recargará automáticamente ¡y mostrará los cambios!

Ten en cuenta que si instalas nuevos plugins como la cámara, seguirás necesitando un rebuild de tu proyecto nativo. Esto se debe a que se modifican archivos nativos y no se puede hacer sobre la marcha.

Nota que debes usar la IP y el puerto correctos en tu configuración. El bloque code de arriba muestra el puerto por defecto de Angular para fines demostrativos.

Uso de Capacitor Plugins

Veamos cómo funciona un plugin Capacitor en acción, como hemos mencionado antes. Para ello, podemos instalar un plugin bastante sencillo ejecutando:

npm i @capacitor/share

No hay nada especial en el plugin Share, pero aun así abre el diálogo nativo de compartir. Para esto ahora solo necesitamos importar el paquete y llamar al correspondiente share() función desde nuestra app, así que cambiemos src/app/app.component.ts a esto:

import { Component } from '@angular/core';
import { Share } from '@capacitor/share';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'my-app';

  async share() {
    await Share.share({
      title: 'Open Youtube',
      text: 'Check new video on youtube',
      url: 'https://www.youtube.com',
      dialogTitle: 'Share with friends'
    });
  }
}

Como mencionamos antes, al instalar nuevos plugins, debemos hacer un sync y luego volver a desplegar la app en nuestro dispositivo. Para ello, ejecuta el siguiente comando:

npx cap sync

¡Tras pulsar el botón, podrás ver el magnífico diálogo nativo de compartir en acción!

Después, puedes hacer que la app se sienta más nativa en iOS y Android con la navegación y las transiciones de Capgo, y solucionar problemas comunes de layout en iOS que causan desbordamiento horizontal o áreas seguras recortadas.

UI con sensación nativa, navegación y transiciones nativas de Capgo

He trabajado durante años con Ionic para crear aplicaciones multiplataforma, pero integrarlo con Angular es poco ortodoxo y rara vez vale la pena cuando ya tienes Tailwind CSS.

Para lograr una sensación nativa en una app de Angular + Capacitor, usa plugins de Capgo en lugar de kits de UI solo web como Konsta UI:

  • @capgo/capacitor-native-navigation — navbar nativa, Liquid Glass tab bar en iOS y un estilo de tab bar con desenfoque en Android. El router de Angular mantiene el estado de las rutas; el plugin controla el chrome nativo.
  • @capgo/capacitor-transitions — transiciones de página estilo Ionic y swipe-back desde el borde en iOS en la capa WebView, sin adoptar Ionic UI.

Instala ambos:

bun add @capgo/capacitor-native-navigation @capgo/capacitor-transitions
bunx cap sync

Configura la navegación nativa con CSS inset mode para que el contenido web respete las barras nativas:

import { NativeNavigation } from '@capgo/capacitor-native-navigation';

await NativeNavigation.configure({
  contentInsetMode: 'css',
  animationDuration: 360,
  glass: {
    effect: 'liquidGlass',
  },
});

Renderiza una tab bar Liquid Glass (iOS usa renderizado del sistema; Android usa un fondo WebView con desenfoque):

import { inject } from '@angular/core';
import { Router } from '@angular/router';

const router = inject(Router);

await NativeNavigation.setTabbar({
  selectedId: 'home',
  labelVisibilityMode: 'labeled',
  icons: true,
  colors: { dynamic: true },
  tabs: [
    { id: 'home', title: 'Home', icon: { svg: '...' } },
    { id: 'settings', title: 'Settings', icon: { svg: '...' } },
  ],
});

await NativeNavigation.addListener('tabSelect', ({ id }) => {
  router.navigate([`/${id}`]);
});

Add native page transitions in your app shell:

```typescript
// app.component.ts
import { Component, CUSTOM_ELEMENTS_SCHEMA, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
import { Router } from '@angular/router';
import '@capgo/capacitor-transitions';
import { initTransitions, setDirection, setupRouterOutlet } from '@capgo/capacitor-transitions';

initTransitions({ platform: 'auto' });

@Component({
  selector: 'app-root',
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  template: `
    <cap-router-outlet #outlet platform="auto" swipe-gesture="auto">
      <router-outlet></router-outlet>
    </cap-router-outlet>
  `,
})
export class AppComponent implements AfterViewInit {
  @ViewChild('outlet') outlet?: ElementRef<HTMLElement>;

  constructor(private router: Router) {}

  ngAfterViewInit() {
    if (this.outlet?.nativeElement) {
      setupRouterOutlet(this.outlet.nativeElement, { platform: 'auto', swipeGesture: 'auto' });
    }
  }

  openSettings() {
    setDirection('forward');
    this.router.navigate(['/settings']);
  }
}

Envuelve las páginas enrutadas en cap-router-outlet, cap-page, y cap-content, y llama a setDirection('forward') o setDirection('back') antes de navegar. No dupliques headers o footers web si la navegación nativa ya gestiona esas áreas.

Consulta las guías completas: Uso de @capgo/capacitor-native-navigation y Uso de @capgo/capacitor-transitions.

Safe areas con Tailwind

Para las safe areas del dispositivo en Tailwind CSS, usa @capgo/tailwind-capacitor (publicado como tailwind-capacitor en npm). Proporciona safe-areas utilidades y otros plugins de Tailwind compatibles con Capacitor:

bun add -D tailwind-capacitor

En src/styles.css:

@import 'tailwindcss';
@plugin "@capgo/tailwind-capacitor/platform";
@plugin "@capgo/tailwind-capacitor/safe-areas";

Usa utilidades como pt-safe, pb-safe, y px-safe en lugar de añadir env(safe-area-inset-*) a mano. El proyecto está en desarrollo activo — si falta algo para tu configuración de Angular, abre un PR en GitHub.

Cómo solucionar problemas de layout en iOS (viewport, safe area y overflow horizontal)

Si el contenido se ve recortado, desplazado o con scroll horizontal en iOS, añadir más overflow-x: hidden o ajustar solo la etiqueta viewport no suele solucionarlo. Revisa estas comprobaciones en orden.

Asegúrate de que la meta tag viewport esté bien aplicada

En src/index.html, configura la etiqueta meta viewport en <head>:

<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />

Gestiona el safe area de iOS desde un único wrapper raíz

Crea un único app shell y aplica el padding de safe area ahí, no en múltiples componentes anidados:

html,
body,
app-root {
  width: 100%;
  min-height: 100%;
  margin: 0;
  padding: 0;
  overflow-x: hidden;
}

* {
  box-sizing: border-box;
}

.app-shell {
  min-height: 100dvh;
  width: 100%;
  padding-top: env(safe-area-inset-top);
  padding-right: env(safe-area-inset-right);
  padding-bottom: env(safe-area-inset-bottom);
  padding-left: env(safe-area-inset-left);
}

Envuelve todo el contenido de la página dentro de .app-shell. El padding de safe-area duplicado en headers, modales y wrappers de layout suele hacer que la UI parezca recortada o demasiado grande.

Con @capgo/tailwind-capacitor, puedes aplicar el mismo padding usando utilidades como pt-safe pb-safe px-safe en ese único shell.

Configura Capacitor iOS contentInset a never primero

En capacitor.config.ts, prefiere desactivar el inset nativo y dejar que CSS (o la navegación nativa) contentInsetMode: 'css') se encargue del safe area:

const config: CapacitorConfig = {
  appId: 'com.example.myapp',
  appName: 'my-app',
  webDir: 'www',
  ios: {
    contentInset: 'never',
  },
};

Combinar el content inset automático de Capacitor con CSS env(safe-area-inset-*) es una causa común de espaciado doble.

Encuentra el elemento que realmente desborda

El culpable habitual es un elemento que usa 100vw, Tailwind w-screen, un ancho fijo en píxeles, o un min-width.

En Safari Web Inspector, ejecuta:

[...document.querySelectorAll('*')]
  .filter(el => el.scrollWidth > document.documentElement.clientWidth)
  .map(el => ({
    el,
    tag: el.tagName,
    class: el.className,
    scrollWidth: el.scrollWidth,
    clientWidth: document.documentElement.clientWidth,
  }));

Con Tailwind, reemplaza w-screen con w-full siempre que sea posible. Muchos problemas de desbordamiento horizontal vienen de 100vw / w-screen, padding de safe-area duplicado o un contenedor de ancho fijo, no del propio meta tag del viewport.

Conclusión

Capacitor es una opción excelente para crear aplicaciones nativas a partir de un proyecto web existente, ya que ofrece una forma sencilla de compartir code y mantener una UI consistente.

Y con la incorporación de Capgo, es aún más fácil añadir live updates a tu app, asegurando que tus usuarios siempre tengan acceso a las últimas funciones y correcciones de errores.

Si quieres aprender a añadir Capgo a tu app de Angular, echa un vistazo al siguiente artículo:

Continúa con Building Mobile Apps with Angular and Capacitor

Si estás usando Cómo crear apps móviles con Angular y Capacitor para planificar el comportamiento de los medios nativos y la interfaz, conéctalo con Uso de @capgo/capacitor-live-activities para la capacidad nativa en Uso de @capgo/capacitor-live-activities, @capgo/capacitor-live-activities para los detalles de implementación en @capgo/capacitor-live-activities, Uso de @capgo/capacitor-video-player para la capacidad nativa en Uso de @capgo/capacitor-video-player, @capgo/capacitor-video-player por el detalle de implementación en @capgo/capacitor-video-player, y Uso de @capgo/capacitor-native-navigation para la capacidad nativa al usar @capgo/capacitor-native-navigation.

Live updates para apps Capacitor

Cuando un bug de la capa web está en producción, publica el fix a través de Capgo en lugar de esperar días a la aprobación de la app store. Los usuarios reciben la actualización en segundo plano mientras los cambios nativos siguen el flujo de revisión habitual.

Lo último de nuestro blog

Capgo te da la información clave que necesitas para crear una app móvil realmente profesional.