Saltar al contenido principal
Tutoriales

Build a Nuxt Mobile App from Scratch with Capacitor 8

Step-by-step guide to creating a new Nuxt 4 project and turning it into native iOS and Android mobile apps using Capacitor 8. Perfect for starting fresh with mobile-first Vue development.

Créditos del artículo

Martin Donadieu

Escribió

Valeria

Revisó

Jordan

Editor

Construye una aplicación móvil de Nuxt desde cero con Capacitor 8

Introducción

¿Quieres crear una aplicación móvil con Nuxt desde cero? Este guía te guía a través de la creación de un proyecto de Nuxt 4 completamente nuevo configurado para móviles desde el principio, y luego empaquetarlo como aplicaciones nativas de iOS y Android utilizando Capacitor 8.

Por el final de esta tutoría, tendrás una aplicación móvil en funcionamiento en simuladores que puedes seguir desarrollando y eventualmente publicar en la Tienda de App y Google Play.

Tiempo requerido: ~30 minutos

¿Qué construirás:

  • Un nuevo proyecto de Nuxt 4 con la estructura de directorios más reciente
  • Configuración de generación estática para móviles
  • Capacitor 8 con plugins esenciales
  • Aplicaciones nativas iOS y Android
  • Configuración de desarrollo con recarga en vivo

Ya tienes una aplicación Nuxt? Consulta Convertir tu aplicación Nuxt en móvil en su lugar.

Requisitos previos

Asegúrate de tener instalados estos:

  • Node.js 18+ (verifica con node --version)
  • Bun administrador de paquetes (curl -fsSL https://bun.sh/install | bash)
  • Xcode (solo para macOS, para desarrollo de iOS)
  • Android Studio (para desarrollo de Android)

Paso 1: Crear un Nuevo Proyecto de Nuxt 4

Comience creando un proyecto de Nuxt 4 fresco:

bunx nuxi@latest init my-mobile-app
cd my-mobile-app
bun install

Estructura de Directorio de Nuxt 4

Nuxt 4 utiliza una nueva estructura de directorio con app code en el directorio: app/ Esta estructura proporciona una mejor separación entre la aplicación y el servidor __CAPGO_KEEP_0__.

my-mobile-app/
  app/
    assets/
    components/
    composables/
    layouts/
    middleware/
    pages/
    plugins/
    utils/
    app.vue
  public/
  server/
  nuxt.config.ts
  package.json

This structure provides better separation between app and server code.

__CAPGO_KEEP_0__ requiere archivos HTML/JS/CSS estáticos. Configure Nuxt para la generación estática en

Capacitor requires static HTML/JS/CSS files. Configure Nuxt for static generation in nuxt.config.ts:

export default defineNuxtConfig({
  compatibilityDate: '2025-01-15',
  devtools: { enabled: true },

  // Enable static generation
  ssr: true,
  nitro: {
    preset: 'static',
  },
});

__CAPGO_KEEP_0__

Actualice su package.json con scripts de desarrollo móvil:

{
  "scripts": {
    "dev": "nuxt dev",
    "build": "nuxt build",
    "generate": "nuxt generate",
    "preview": "nuxt preview",
    "mobile": "bun run generate && bunx cap sync",
    "mobile:ios": "bun run mobile && bunx cap open ios",
    "mobile:android": "bun run mobile && bunx cap open android"
  }
}

Pruebe la generación estática:

bun run generate

Debería ver un .output/public con sus archivos estáticos.

Paso 4: Instale Capacitor 8

Instale los paquetes de Capacitor básicos:

bun add @capacitor/core
bun add -D @capacitor/cli

Instale plugins esenciales que la mayoría de las aplicaciones móviles necesitan:

bun add @capacitor/app @capacitor/keyboard @capacitor/splash-screen @capacitor/status-bar @capacitor/preferences

¿Qué hacen estos plugins?

  • @capacitor/app — Eventos de ciclo de vida de la aplicación (anterior/plano, enlaces profundos)
  • @capacitor/keyboard — Controlar el comportamiento del teclado
  • @capacitor/pantalla-de-bienvenida — Control de pantalla de bienvenida nativa
  • @capacitor/barra-de-estado — Estilizar la barra de estado del dispositivo
  • @capacitor/preferencias — Almacenamiento de valores clave (como localStorage pero nativo)

Paso 5: Inicializar Capacitor

Inicialice Capacitor con los detalles de su proyecto:

bunx cap init "My Mobile App" com.example.mymobileapp --web-dir .output/public

Sustituya:

  • "My Mobile App" por el nombre de pantalla de su aplicación
  • com.example.mymobileapp por el ID de su aplicación (notación de dominio inverso)

Esto crea capacitor.config.tsActualice con la configuración del plugin:

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

const config: CapacitorConfig = {
  appId: 'com.example.mymobileapp',
  appName: 'My Mobile App',
  webDir: '.output/public',
  plugins: {
    SplashScreen: {
      launchShowDuration: 2000,
      launchAutoHide: true,
      androidScaleType: 'CENTER_CROP',
      splashFullScreen: true,
      splashImmersive: true,
    },
    Keyboard: {
      resize: 'body',
      resizeOnFullScreen: true,
    },
    StatusBar: {
      style: 'dark',
    },
  },
};

export default config;

Paso 6: Agregue plataformas nativas

Instale los paquetes de plataforma:

bun add @capacitor/ios @capacitor/android

Genere los proyectos nativos:

bunx cap add ios
bunx cap add android

Esto crea ios y android directorio que contiene los proyectos nativos.

Paso 7: Compilar y Ejecutar

Compile su proyecto y sincronice con las plataformas nativas:

bun run mobile

Abrir en iOS Simulator:

bun run mobile:ios

O Emulador de Android:

bun run mobile:android

In Xcode (iOS):

  1. Seleccione un simulador desde el menú desplegable de dispositivos
  2. Haga clic en el botón de reproducción o presione Cmd + R

In Android Studio:

  1. Espera a que Gradle termine de sincronizar
  2. Seleccione un emulador desde el menú desplegable de dispositivos
  3. Haga clic en el botón de ejecución o presione Shift + F10

Paso 8: Configuración de Live Reload

Para un desarrollo más rápido, habilite la reproducción en vivo para que los cambios aparezcan instantáneamente en su dispositivo.

  1. Encuentre su dirección IP local:
# macOS
ipconfig getifaddr en0

# Windows
ipconfig
  1. Cree una configuración de desarrollo Capacitor. Actualice capacitor.config.ts:
import type { CapacitorConfig } from '@capacitor/cli';

const devConfig: CapacitorConfig = {
  appId: 'com.example.mymobileapp',
  appName: 'My Mobile App',
  webDir: '.output/public',
  server: {
    url: 'http://YOUR_IP_ADDRESS:3000',
    cleartext: true,
  },
  plugins: {
    // ... same plugin config
  },
};

const prodConfig: CapacitorConfig = {
  appId: 'com.example.mymobileapp',
  appName: 'My Mobile App',
  webDir: '.output/public',
  plugins: {
    // ... same plugin config
  },
};

const config = process.env.NODE_ENV === 'development' ? devConfig : prodConfig;

export default config;
  1. Inicie el servidor de desarrollo y copie la configuración a nativo:
bun run dev &
NODE_ENV=development bunx cap copy
  1. Reconstruir en Xcode/Android Studio

Ahora los cambios en tu Nuxt code se recargarán automáticamente en el dispositivo.

Paso 9: Crea tu primera pantalla móvil

Vamos a crear una pantalla de inicio amigable con dispositivos móviles. Actualizar app/app.vue:

<template>
  <NuxtPage />
</template>

Crear app/pages/index.vue:

<template>
  <main
    class="min-h-screen bg-linear-to-b from-green-500 to-green-700 flex flex-col items-center justify-center p-6 text-white"
  >
    <h1 class="text-4xl font-bold mb-4">My Mobile App</h1>
    <p class="text-xl mb-8 text-center opacity-90">
      Built with Nuxt 4 + Capacitor 8
    </p>

    <div v-if="appInfo" class="bg-white/20 rounded-lg p-4 backdrop-blur-sm mb-8">
      <p class="text-sm">
        {{ appInfo.name }} v{{ appInfo.version }}
      </p>
    </div>

    <div class="space-y-4 w-full max-w-sm">
      <button
        class="w-full py-4 px-6 bg-white text-green-600 rounded-xl font-semibold text-lg shadow-lg active:scale-95 transition-transform"
        @click="handleGetStarted"
      >
        Get Started
      </button>
      <button
        class="w-full py-4 px-6 bg-white/20 text-white rounded-xl font-semibold text-lg backdrop-blur-sm active:scale-95 transition-transform"
        @click="handleShare"
      >
        Share App
      </button>
    </div>
  </main>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';
import { App } from '@capacitor/app';

const appInfo = ref<{ name: string; version: string } | null>(null);

let backButtonListener: { remove: () => void } | null = null;

onMounted(async () => {
  // Get app info
  try {
    appInfo.value = await App.getInfo();
  } catch (e) {
    // Web fallback
    appInfo.value = { name: 'My Mobile App', version: '1.0.0' };
  }

  // Handle Android back button
  backButtonListener = await App.addListener('backButton', ({ canGoBack }) => {
    if (!canGoBack) {
      App.exitApp();
    } else {
      window.history.back();
    }
  });
});

onUnmounted(() => {
  backButtonListener?.remove();
});

function handleGetStarted() {
  // Navigate to onboarding or main app
  console.log('Get started clicked');
}

async function handleShare() {
  // We'll implement this with the Share plugin later
  console.log('Share clicked');
}
</script>

Paso 10: Agrega Tailwind CSS

Para que el estilo funcione, agrega Tailwind CSS a tu proyecto:

bun add tailwindcss @tailwindcss/vite

Actualizar nuxt.config.ts:

import tailwindcss from '@tailwindcss/vite';

export default defineNuxtConfig({
  compatibilityDate: '2025-01-15',
  devtools: { enabled: true },

  ssr: true,
  nitro: {
    preset: 'static',
  },

  css: ['~/assets/css/main.css'],

  vite: {
    plugins: [tailwindcss()],
  },
});

Crear app/assets/css/main.css:

@import 'tailwindcss';

:root {
  --sat: env(safe-area-inset-top);
  --sar: env(safe-area-inset-right);
  --sab: env(safe-area-inset-bottom);
  --sal: env(safe-area-inset-left);
}

body {
  padding-top: var(--sat);
  padding-right: var(--sar);
  padding-bottom: var(--sab);
  padding-left: var(--sal);
}

/* Prevent text selection on mobile */
* {
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}

/* Allow text selection in inputs */
input,
textarea {
  -webkit-user-select: auto;
  user-select: auto;
}

Paso 11: Agrega el plugin de compartir

Vamos a implementar la funcionalidad del botón de compartir:

bun add @capacitor/share

Actualizar app/pages/index.vue para usar el plugin de Compartir:

<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';
import { App } from '@capacitor/app';
import { Share } from '@capacitor/share';

// ... existing code ...

async function handleShare() {
  try {
    await Share.share({
      title: 'Check out this app!',
      text: 'Built with Nuxt 4 and Capacitor 8',
      url: 'https://capacitorjs.com',
      dialogTitle: 'Share with friends',
    });
  } catch (e) {
    console.log('Share cancelled or failed:', e);
  }
}
</script>

Sync y reconstruir:

bun run mobile

Estructura del Proyecto

Su proyecto debería verse así:

my-mobile-app/
├── android/                  # Android native project
├── ios/                      # iOS native project
├── .output/
│   └── public/              # Static build output
├── app/
│   ├── assets/
│   │   └── css/
│   │       └── main.css
│   ├── pages/
│   │   └── index.vue
│   └── app.vue
├── capacitor.config.ts       # Capacitor configuration
├── nuxt.config.ts            # Nuxt configuration
├── package.json
└── ...

Pasos siguientes

Ahora tiene una aplicación móvil de Nuxt funcionando. Aquí está qué hacer a continuación:

Configuración Esencial

  • Iconos de la Aplicación: Sustituir iconos predeterminados en ios/App/App/Assets.xcassets y android/app/src/main/res
  • Pantalla de bienvenida: Personalizar en proyectos nativos o usar @capacitor/splash-screen config
  • Enlaces Profundos: Configurar esquemas de URL para tu aplicación

Agregar Más Características

  • Cámara: bun add @capacitor/camera
  • Ubicación: bun add @capacitor/geolocation
  • Notificaciones Push: bun add @capacitor/push-notifications o @capgo/capacitor-firebase-messaging para Firebase Cloud Messaging en iOS y Android
  • Sistema de Archivos: bun add @capacitor/filesystem

Interfaz de usuario nativa y transiciones

Utilice Capgo plugins en lugar de Konsta UI para un sentir móvil nativo:

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

Para áreas seguras de Tailwind, agregue @capgo/tailwind-capacitor:

bun add -D tailwind-capacitor

Consulte Usando @capgo/capacitor-navegación-nativa, Usando @capgo/capacitor-transiciones, y el repo tailwind-capacitor para la configuración específica de Nuxt.

Solucionar problemas de diseño de iOS (Viewport, Área Segura y Desbordamiento Horizontal)

Si el contenido parece recortado, desplazado o desplazable horizontalmente en iOS, agregar más overflow-x: hidden o ajustar la etiqueta de viewport sola no suele solucionar el problema. Pase por estas comprobaciones en orden.

Asegúrese de que la etiqueta meta de viewport se aplique correctamente

En nuxt.config.ts, establezca el viewport a través de app.head:

export default defineNuxtConfig({
  app: {
    head: {
      meta: [
        {
          name: 'viewport',
          content: 'width=device-width, initial-scale=1, viewport-fit=cover',
        },
      ],
    },
  },
});

Gestione el área seguro de iOS desde un solo contenedor raíz

Cree una sola caja de aplicación y aplique allí el relleno del área segura — no en múltiples componentes anidados:

html,
body,
#__nuxt {
  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);
}

Envuelva todo el contenido de la página dentro de .app-shellEl relleno de área segura duplicado en encabezados, modales y contenedores de diseño a menudo hace que la interfaz de usuario parezca recortada o demasiado grande.

Con @capgo/tailwind-capacitor, puedes expresar el mismo relleno con utilidades como pt-safe pb-safe px-safe en esa sola caja.

Establece Capacitor iOS contentInset a never primero

En capacitor.config.ts, prefiere el relleno nativo deshabilitado y deja que CSS (o la navegación nativa) contentInsetMode: 'css') tenga el área segura:

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

Mezclar Capacitor’s contenido automático con CSS env(safe-area-inset-*) relleno es una causa común de doble espaciado.

Encuentra el elemento que rebosa realmente

The usual culprit is an element using 100vw, Tailwind w-screen, un ancho de píxel fijo, o un contenedor muy ancho min-width.

In Safari Web Inspector, run:

[...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 cuando sea posible. Muchos problemas de rebosamiento horizontal provienen de 100vw / w-screen, relleno de área segura duplicado, o un contenedor de ancho fijo — no del meta de viewport en sí mismo.

Actualizaciones por Vía Aérea

Configura Capgo para enviar actualizaciones sin la necesidad de volver a presentar la aplicación en la tienda:

bunx @capgo/cli init

Resolución de problemas

La construcción falla con "No se puede encontrar el módulo" Ejecutar bun install y intentarlo de nuevo.

iOS: "No se encontró identidad de firma" Abra Xcode, vaya a Firmas y capacidades, y seleccione su equipo de desarrollo.

Android: "SDK no se encontró en la ubicación" Crear android/local.properties con sdk.dir=/path/to/android/sdk

Los cambios no se muestran en el dispositivo Asegúrese de haber ejecutado bun run mobile después de hacer cambios. Para la recarga en vivo, verifique que la dirección IP es correcta y que el servidor de desarrollo está en ejecución.

.output/public está vacío o faltante Asegúrate de que configuraste nitro: { preset: 'static' } en nuxt.config.ts y ejecuta bun run generate.

Recursos

¿Listo para enviar tu aplicación? Aprende cómo Capgo puede ayudarte a entregar actualizaciones más rápido — regístrese para una cuenta gratuita hoy.

Siga adelante desde Construya una aplicación móvil de Nuxt desde cero con Capacitor 8

Si está utilizando Construya una aplicación móvil de Nuxt desde cero con Capacitor 8 para planificar la automatización de CI/CD, conecte con Capgo CI/CD para el flujo de trabajo del producto en Capgo CI/CD, Capgo Compilaciones nativas para el flujo de trabajo del producto en Capgo Compilaciones nativas, Capgo Integraciones for the product workflow in Capgo Integrations, Integración CI/CD para los detalles de implementación en Integración CI/CD GitHub Integración de Acciones para los detalles de implementación en GitHub Integración de Acciones

Actualizaciones en vivo para aplicaciones Capacitor

Cuando un error de capa web está vivo, envía la corrección a través de Capgo en lugar de esperar días para la aprobación de la tienda de aplicaciones. Los usuarios obtienen la actualización en segundo plano mientras los cambios nativos siguen en el camino de revisión normal.

soporte humano de Martin

Inicia Ahora

Últimas noticias de nuestro Blog

Capgo te da las mejores perspectivas que necesitas para crear una aplicación móvil verdaderamente profesional.