Introducción
¿Quieres crear una aplicación móvil con Nuxt desde cero? Esta guía te guía a través de la creación de un proyecto de Nuxt 4 configurado para móvil desde el primer día, luego empaquetarlo como aplicaciones móviles nativas de iOS y Android utilizando Capacitor 8.
Al final de este tutorial, 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 directorio más reciente
- Configuración de generación estática para móviles
- Capacitor 8 con plugins esenciales
- Aplicaciones nativas de iOS y Android
- Configuración de desarrollo con recarga en vivo
Ya tienes una aplicación de Nuxt? Consulta Convierte tu aplicación de Nuxt a móvil en su lugar.
Requisitos previos
Asegúrate de tener instalados los siguientes:
- 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
Comienza 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 app/ directorios:
my-mobile-app/
app/
assets/
components/
composables/
layouts/
middleware/
pages/
plugins/
utils/
app.vue
public/
server/
nuxt.config.ts
package.json
Esta estructura proporciona una mejor separación entre la aplicación y el servidor code.
Paso 2: Configurar Nuxt para la Generación Estática
Capacitor requiere archivos HTML/JS/CSS estáticos. Configura Nuxt para la generación estática en nuxt.config.ts:
export default defineNuxtConfig({
compatibilityDate: '2025-01-15',
devtools: { enabled: true },
// Enable static generation
ssr: true,
nitro: {
preset: 'static',
},
});
Paso 3: Agregar Scripts Móviles
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 directorios con sus archivos estáticos.
Paso 4: Instale Capacitor 8
Instale los paquetes de núcleo Capacitor:
bun add @capacitor/core
bun add -D @capacitor/cli
Instale los 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 — Control del comportamiento del teclado
- @capacitor/splash-screen — Control de pantalla de bienvenida nativa
- @capacitor/status-bar — Estilizar la barra de estado del dispositivo
- @capacitor/preferences — Almacenamiento de valor clave (como localStorage pero nativo)
Step 5: Inicializa Capacitor
Inicializa Capacitor con los detalles de tu proyecto:
bunx cap init "My Mobile App" com.example.mymobileapp --web-dir .output/public
Sustituye:
"My Mobile App"por el nombre de pantalla de tu aplicacióncom.example.mymobileapppor el ID de tu aplicación (notación de dominio inverso)
Esto crea capacitor.config.tsActualízalo 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;
Step 6: Agrega Plataformas Nativas
Instala los paquetes de plataforma:
bun add @capacitor/ios @capacitor/android
Genera los proyectos nativos:
bunx cap add ios
bunx cap add android
Esto crea ios y los directorios que contienen los proyectos nativos. android Paso 7: Compilar y Ejecutar
Construya su proyecto y sincronice con las plataformas nativas:
Abrir en Simulador de iOS:
bun run mobile
O Emulador de Android:
bun run mobile:ios
En Xcode (iOS):
bun run mobile:android
Seleccione un simulador desde el menú de dispositivos
- Haga clic en el botón de reproducción o presione
- En Android Studio:
Cmd + R
Esperar a que Gradle termine de sincronizar
- Seleccione un emulador desde el menú de dispositivos
- Esperar a que se sincronice con la plataforma nativa
- Haga clic en el botón Ejecutar o presione
Shift + F10
Paso 8: Configura Live Reload
Para un desarrollo más rápido, habilite el live reload para que los cambios aparezcan instantáneamente en su dispositivo.
- Encuentre su dirección IP local:
# macOS
ipconfig getifaddr en0
# Windows
ipconfig
- Cree un archivo de 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;
- Inicie el servidor de desarrollo y copie la configuración a nativo:
bun run dev &
NODE_ENV=development bunx cap copy
- Reconstruya en Xcode/Android Studio
Ahora los cambios en su Nuxt code se recargarán automáticamente en el dispositivo.
Paso 9: Crea la primera pantalla móvil
Vamos a crear una pantalla de inicio amigable para móviles. Actualice 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: Agregue 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
Implementemos 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>
Sincroniza y reconstruye:
bun run mobile
Estructura del Proyecto
Tu proyecto debería verse así ahora:
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 tienes una aplicación móvil de Nuxt funcionando. Aquí está qué hacer a continuación:
Configuración Básica
- Iconos de Aplicación: Sustituir iconos predeterminados en
ios/App/App/Assets.xcassetsyandroid/app/src/main/res - Pantalla de Inicio: Personalizar en proyectos nativos o usar
@capacitor/splash-screenconfig - 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 de Push:
bun add @capacitor/push-notificationso @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 los plugins Capgo en lugar de Konsta UI para un sentir móvil nativo:
- @capgo/capacitor-native-navigation — barra de pestañas de vidrio líquido y navbar nativa
- @capgo/capacitor-transiciones — transiciones de página con un sentir 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
Ver Usando @capgo/capacitor-navegación nativa, Usando @capgo/capacitor-transicionesy la repo de tailwind-capacitor para la configuración específica de Nuxt.
Resolviendo 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 estos controles en orden.
Asegúrese de que la etiqueta meta de viewport se aplique correctamente
En nuxt.config.tsEstablecer la vista a través de app.head:
export default defineNuxtConfig({
app: {
head: {
meta: [
{
name: 'viewport',
content: 'width=device-width, initial-scale=1, viewport-fit=cover',
},
],
},
},
});
Gestionar la zona segura de iOS desde un solo envoltorio raíz
Crear una caja de aplicación única y aplicar allí el relleno de zona 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);
}
Envolver todo el contenido de la página dentro de .app-shellEl relleno de zona segura duplicado en encabezados, modales y envoltorios de diseño a menudo hace que la interfaz de usuario se vea recortada o demasiado grande.
Con @capgo/tailwind-capacitor, puedes expresar el mismo relleno con utilidades como pt-safe pb-safe px-safe en esa caja única.
Establecer Capacitor iOS contentInset a never primero
Inicia con el área de seguridad deshabilitada y deja que CSS (o la navegación nativa) se encargue del área segura: capacitor.config.tsMezclar el contenido de área de seguridad automático de __CAPGO_KEEP_0__ con CSS de relleno es una causa común de espaciado doble. contentInsetMode: 'css'Encuentra el elemento que está desbordando realmente
const config: CapacitorConfig = {
appId: 'com.example.myapp',
appName: 'my-app',
webDir: '.output/public',
ios: {
contentInset: 'never',
},
};
Mixing Capacitor’s automatic content inset with CSS env(safe-area-inset-*) , Tailwind
, una anchura de píxeles fija, o un tamaño muy grande
In Safari Web Inspector, ejecuta: 100vwCon Tailwind, reemplaza w-screencon min-width.
Preferir el área de seguridad nativa deshabilitada y dejar que CSS (o navegación nativa) se encargue del área segura:
[...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,
}));
Mezclar el contenido de área de seguridad automático de __CAPGO_KEEP_0__ con CSS de relleno es una causa común de espaciado doble. w-screen Encuentra el elemento que está desbordando realmente. w-full When sea posible. Muchos problemas de rebosamiento horizontal provienen de 100vw / w-screen, duplicado relleno de zona segura, o un contenedor de ancho fijo — no del meta etiqueta de viewport en sí.
Actualizaciones por Vía Aérea
Configuración Capgo para enviar actualizaciones sin la necesidad de volver a subir la aplicación a la tienda:
bunx @capgo/cli init
Resolución de Problemas
La compilación falla con ‘No se puede encontrar el módulo’
Ejecutar bun install y vuelve a intentarlo.
iOS: ‘No se encontró identidad de firma’ Abre Xcode, ve a Firmas y Capabilities, y selecciona tu equipo de desarrollo.
Android: “SDK ubicación no encontrada”
Crear android/local.properties con sdk.dir=/path/to/android/sdk
Los cambios no aparecen en el dispositivo
Asegúrate de haber ejecutado bun run mobile después de hacer cambios. Para el recarga en vivo, verifica que la dirección IP es correcta y el servidor de desarrollo está en ejecución.
Es vacío o falta .output/public
Asegúrate de haber configurado nitro: { preset: 'static' } en nuxt.config.ts y ejecutar bun run generate.
Recursos
- Capacitor 8 Documentación
- Documentación de Nuxt 4
- Capgo - Actualizaciones en vivo
- @capgo/capacitor-navegación nativa
- @capgo/capacitor-transiciones
- @capgo/tailwind-capacitor
¿Estás listo para enviar tu aplicación? Aprende cómo Capgo puede ayudarte a entregar actualizaciones más rápido — regístrate para una cuenta gratuita hoy.
Sigue adelante desde Construye una aplicación móvil de Nuxt desde cero con Capacitor 8
Si estás utilizando Construye una aplicación móvil de Nuxt desde cero con Capacitor 8 para planificar la automatización de CI/CD, conecta 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 para el flujo de trabajo del producto en Capgo Integraciones Integración CI/CD para el detalle de implementación en Integración CI/CD, y GitHub Integración de Acciones para el detalle de implementación en GitHub Integración de Acciones