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 primer día, y luego empaquetarlo como aplicaciones nativas de iOS y Android utilizando Capacitor 8.
Al final de esta tutoría, tendrás una aplicación móvil en funcionamiento que corre 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 with essential plugins
- Aplicaciones nativas de iOS y Android
- Configuración de desarrollo con recarga en vivo
Ya tienes una aplicación de Nuxt? Convierte tu aplicación de Nuxt a 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 el 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',
},
});
Actualice su
con scripts de desarrollo móvil: package.json Nuxt 4 uses a new directory structure with app __CAPGO_KEEP_0__ in the directory: -> Nuxt 4 utiliza una nueva estructura de directorio con app __CAPGO_KEEP_0__ en el directorio: -> (no se traduce el título de la sección, solo el texto)
{
"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"
}
}
Prueba la generación estática:
bun run generate
Deberías ver un .output/public directorio con tus archivos estáticos.
Paso 4: Instala Capacitor 8
Instala los paquetes de core de Capacitor:
bun add @capacitor/core
bun add -D @capacitor/cli
Instala 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 — Controla el comportamiento del teclado
- @capacitor/splash-screen — Control de pantalla de arranque nativa
- @capacitor/estado-de-la-pantalla — Estiliza la barra de estado del dispositivo
- @capacitor/preferencias — Almacenamiento de valores clave (como localStorage pero nativo)
Paso 5: Inicializa Capacitor
Inicializa Capacitor con 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.ts. Actualiza 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 android directorios que contienen los proyectos nativos.
Step 7: Compila y Ejecuta
Compila tu proyecto y sincroniza con plataformas nativas:
bun run mobile
Abre en Simulador de iOS:
bun run mobile:ios
O Emulador de Android:
bun run mobile:android
En Xcode (iOS):
- Selecciona un simulador desde el menú de dispositivos
- Haga clic en el botón de reproducción o presione
Cmd + R
In Android Studio:
- Espera a que Gradle termine de sincronizar
- Seleccione un emulador desde el menú desplegable de dispositivos
- 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 reactivación en vivo para que los cambios aparezcan instantáneamente en su dispositivo.
- Encuentre su dirección IP local:
# macOS
ipconfig getifaddr en0
# Windows
ipconfig
- Crear 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 reactivarán en vivo en el dispositivo.
Step 9: Crea tu primera pantalla móvil
Vamos a crear una pantalla de inicio amigable con 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>
Step 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;
}
Step 11: Agrega el plugin de Compartir
Vamos a implementar la función de 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>
Sincronizar y reconstruir:
bun run mobile
estructura del proyecto
Su 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 tiene una aplicación móvil de Nuxt funcionando. Aquí está lo que debe hacer a continuación:
Configuración esencial
- Iconos de la aplicación: Sustituir iconos predeterminados en
ios/App/App/Assets.xcassetsyandroid/app/src/main/res - Pantalla de bienvenida: Personalizar en proyectos nativos o usar
@capacitor/splash-screenconfiguración - Enlaces profundos: Configura 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 ¿Qué características adicionales necesitas? - ¿Qué características adicionales necesitas?
bun add @capacitor/filesystem
¿Qué características adicionales necesitas?
Use Capgo plugins instead of Konsta UI for a native mobile feel:
- @capgo/capacitor-firebase-messaging para Firebase Cloud Messaging en iOS y Android — Barra de navegación de vidrio líquido y barra de navegación nativa
- @capgo/capacitor-transiciones — Transiciones de página que se sienten nativas
bun add @capgo/capacitor-native-navigation @capgo/capacitor-transitions
bunx cap sync
Para áreas seguras de Tailwind, agrega @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.
Solucionando problemas de diseño en iOS (Vista previa, área segura y desbordamiento horizontal)
If el contenido parece recortado, desplazado o desplazable horizontalmente en iOS, agregar más overflow-x: hidden o ajustar la etiqueta de viewport en sí solo no suele solucionar el problema. Trabaja a través de estas comprobaciones en orden.
Asegúrate de que la etiqueta meta de viewport se aplique correctamente
In nuxt.config.ts, establece 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',
},
],
},
},
});
Gestiona el área segura de iOS desde un solo contenedor raíz
Crear un solo contenedor de aplicación y aplicar allí el relleno de á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);
}
Envuelve 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 ese solo shell.
Establecer Capacitor iOS contentInset hacia never primero
En capacitor.config.ts, prefiere el deshabilitado nativo y deja que CSS (o la navegación nativa) contentInsetMode: 'css'propia de la zona segura:
const config: CapacitorConfig = {
appId: 'com.example.myapp',
appName: 'my-app',
webDir: '.output/public',
ios: {
contentInset: 'never',
},
};
Mezclar el contenido de Capacitor automatico con CSS env(safe-area-inset-*) es una causa común de doble espacio.
Encuentra el elemento que está desbordando en realidad
El culpable usual es un elemento que utiliza 100vw, Tailwind w-screenuna anchura de píxel fija, o una gran min-width.
Inicia en el Inspector de Safari:
[...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,
}));
Sustituye w-screen con w-full cuando sea posible. Muchos problemas de rebosamiento horizontal provienen de 100vw / w-screenduplicado relleno de área segura, o un contenedor de anchura fija — no del etiqueta meta de viewport en sí.
Actualizaciones de Línea de Comando
Configura Capgo para enviar actualizaciones sin tener que volver a presentar la aplicación en la tienda:
bunx @capgo/cli init
Solución de Problemas
Configuración de la Aplicación falla con ‘No se puede encontrar el módulo’
Ejecutar bun install y vuelve a intentarlo.
iOS: “No se encontró ninguna identidad de firma” Abre Xcode, ve a Signing & Capabilities, y selecciona tu 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ú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 que el servidor de desarrollo está en ejecución.
.output/public está vacío o faltante
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
¿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.
Continúa desde Crea una aplicación móvil de Nuxt desde cero con Capacitor 8
Si estás utilizando Crea 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, Integraciones Integración de CI/CD GitHub Acciones de Integración para los detalles de implementación en GitHub Acciones de Integración.