Salta al contenuto principale
Tutorial

Crea un'app mobile Nuxt da zero con Capacitor 8

Guida passo dopo passo per creare un nuovo progetto Nuxt 4 e trasformarlo in app native iOS e Android con Capacitor 8. Ideale per iniziare da zero con lo sviluppo Vue mobile-first.

Crea un'app mobile Nuxt da zero con Capacitor 8 Capgo illustrazione del blog

Introduzione

Vuoi creare un'app mobile con Nuxt partendo da zero? Questa guida ti accompagna nella creazione di un nuovo progetto Nuxt 4 configurato per il mobile fin dal primo giorno, per poi generare le app native iOS e Android usando Capacitor 8.

Al termine di questo tutorial avrai un'app mobile funzionante sui simulatori, che potrai continuare a sviluppare e infine pubblicare su App Store e Google Play.

Tempo richiesto: ~30 minuti

Cosa creerai:

  • Un nuovo progetto Nuxt 4 con la struttura di directory più recente
  • Configurazione della generazione statica per mobile
  • Capacitor 8 con i plugin essenziali
  • App native iOS e Android
  • Setup di sviluppo con Live reload

Hai già un'app Nuxt? Dai un'occhiata a Converti la tua app Nuxt in mobile invece.

Prerequisiti

Assicurati di avere installato:

  • Node.js 18+ (verifica con node --version)
  • Bun package manager ("curl -fsSL https://bun.sh/install | bash)
  • Xcode (solo macOS, per lo sviluppo iOS)
  • Android Studio (per lo sviluppo Android)

Passo 1: Crea un nuovo progetto Nuxt 4

Inizia creando un nuovo progetto Nuxt 4:

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

Struttura delle directory di Nuxt 4

Nuxt 4 utilizza una nuova struttura di directory con l'app code nella app/ directory:

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

Questa struttura offre una migliore separazione tra app e server code.

Passaggio 2: Configura Nuxt per la generazione statica

Capacitor richiede file HTML/JS/CSS statici. Configura Nuxt per la generazione statica nella nuxt.config.ts:

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

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

Passaggio 3: Aggiungi gli script per mobile

Aggiorna il tuo package.json con gli script per lo sviluppo mobile:

{
  "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"
  }
}

Testa la generazione statica:

bun run generate

Dovresti vedere una .output/public directory con i tuoi file statici.

Passaggio 4: Installa Capacitor 8

Installa i pacchetti core di Capacitor:

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

Installa i plugin essenziali che la maggior parte delle app mobili richiede:

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

Cosa fanno questi plugin:

  • @capacitor/app — Eventi del ciclo di vita dell'app (foreground/background, deep link)
  • @capacitor/keyboard — Controllo del comportamento della tastiera
  • @capacitor/splash-screen — Controllo dello splash screen nativo
  • @capacitor/status-bar — Personalizza la status bar del dispositivo
  • @capacitor/preferences — Storage key-value (simile a localStorage ma nativo)

Passaggio 5: Inizializza Capacitor

Inizializza Capacitor con i dettagli del progetto:

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

Sostituisci:

  • "My Mobile App" con il nome visualizzato della tua app
  • com.example.mymobileapp con l'ID della tua app (notazione reverse domain)

Questo crea capacitor.config.ts. Aggiornalo con la configurazione 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;

Passaggio 6: Aggiungi piattaforme native

Installa i pacchetti della piattaforma:

bun add @capacitor/ios @capacitor/android

Genera i progetti nativi:

bunx cap add ios
bunx cap add android

Questo crea ios e android directory contenenti i progetti nativi.

Passaggio 7: Build ed esegui

Compila il progetto e sincronizza con le piattaforme native:

bun run mobile

Apri nel Simulatore iOS:

bun run mobile:ios

Oppure nell'Emulatore Android:

bun run mobile:android

In Xcode (iOS):

  1. Seleziona un simulatore dal menu a tendina dei dispositivi
  2. Clicca sul pulsante Play o premi Cmd + R

In Android Studio:

  1. Aspetta che Gradle completi la sincronizzazione
  2. Seleziona un emulatore dal menu a tendina dei dispositivi
  3. Clicca sul pulsante Run o premi Shift + F10

Passaggio 8: Configura Live Reload

Per uno sviluppo più rapido, attiva il live reload per vedere le modifiche istantaneamente sul dispositivo.

  1. Trova il tuo indirizzo IP locale:
# macOS
ipconfig getifaddr en0

# Windows
ipconfig
  1. Crea una config di sviluppo Capacitor. Aggiorna 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. Avvia il dev server e copia la config nel nativo:
bun run dev &
NODE_ENV=development bunx cap copy
  1. Ricostruisci in Xcode/Android Studio

Ora le modifiche al tuo Nuxt code verranno ricaricate in tempo reale sul dispositivo.

Passaggio 9: Crea la tua prima schermata mobile

Creiamo una home screen adatta al mobile. Aggiorna app/app.vue:

<template>
  <NuxtPage />
</template>

Crea 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>

Passo 10: Aggiungi Tailwind CSS

Per far funzionare lo styling, aggiungi Tailwind CSS al tuo progetto:

bun add tailwindcss @tailwindcss/vite

Aggiornamento 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()],
  },
});

Crea 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;
}

Passo 11: Aggiungi il plugin Share

Implementiamo la funzionalità del pulsante di condivisione:

bun add @capacitor/share

Aggiornamento app/pages/index.vue per usare il plugin Share:

<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>

Sincronizza e ricompila:

bun run mobile

Struttura del progetto

Il tuo progetto ora dovrebbe apparire così:

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
└── ...

Prossimi passi

Ora hai un'app mobile Nuxt funzionante. Ecco cosa fare dopo:

Setup essenziale

  • Icone dell'app: Sostituisci le icone predefinite in ios/App/App/Assets.xcassets e android/app/src/main/res
  • Splash screen: Personalizza nei progetti nativi o usa @capacitor/splash-screen configurazione
  • Deep Link: Configura gli schemi URL per la tua app

Aggiungi altre feature

  • Fotocamera: bun add @capacitor/camera
  • Geolocalizzazione: bun add @capacitor/geolocation
  • Notifiche push: bun add @capacitor/push-notifications oppure @capgo/capacitor-firebase-messaging per Firebase Cloud Messaging su iOS e Android
  • File System: bun add @capacitor/filesystem

UI nativa e transizioni

Usa i plugin Capgo al posto di Konsta UI per un'esperienza mobile nativa:

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

Per le safe area di Tailwind, aggiungi @capgo/tailwind-capacitor:

bun add -D tailwind-capacitor

Vedi Usando @capgo/capacitor-native-navigation, Uso di @capgo/capacitor-transitions, e il la repo tailwind-capacitor per la configurazione specifica di Nuxt.

Risolvere problemi di layout su iOS (Viewport, Safe Area e overflow orizzontale)

Se i contenuti appaiono tagliati, spostati o con scroll orizzontale su iOS, aggiungere più overflow-x: hidden o modificare il tag viewport di solito non basta. Segui questi controlli in ordine.

Assicurati che il meta tag viewport sia applicato correttamente

In nuxt.config.ts, imposta il viewport tramite app.head:

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

Gestisci la safe area di iOS da un unico wrapper root

Crea un unico app shell e applica lì il padding per la safe area — non in più componenti annidati:

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);
}

Racchiudi tutto il contenuto della pagina in .app-shell. Il padding della safe-area duplicato in header, modal e wrapper del layout spesso fa apparire l'interfaccia tagliata o troppo grande.

Con @capgo/tailwind-capacitor, puoi applicare lo stesso padding con utility come pt-safe pb-safe px-safe su quella singola shell.

Imposta Capacitor iOS contentInset a never per primo

In capacitor.config.ts, preferisci disabilitare i native inset e lascia che il CSS (o quella contentInsetMode: 'css'di Native Navigation) gestisca la safe area:

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

Mescolare l'automatic content inset di Capacitor con il CSS env(safe-area-inset-*) il padding è una causa comune di spaziature doppie.

Trova il vero elemento che causa l'overflow

Il colpevole di solito è un elemento che usa 100vw, Tailwind w-screen, una larghezza fissa in pixel o un grande min-width.

In Safari Web Inspector, esegui:

[...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, sostituisci w-screen con w-full quando possibile. Molti problemi di overflow orizzontale derivano da 100vw / w-screen, padding della safe-area duplicato o un contenitore a larghezza fissa — non dal meta tag viewport stesso."

Aggiornamenti Over-the-Air

Configurazione Capgo per pubblicare aggiornamenti senza doverli risottomettere agli store:

bunx @capgo/cli init

Risoluzione problemi

La build fallisce con “Cannot find module” Esegui bun install e riprova.

iOS: “Identità di firma non trovata” Apri Xcode, vai su Signing &amp; Capabilities e seleziona il tuo team di sviluppo.

Android: “SDK non trovato” Crea android/local.properties con sdk.dir=/path/to/android/sdk

Le modifiche non appaiono sul dispositivo Assicurati di aver eseguito bun run mobile dopo le modifiche. Per il live reload, verifica che l'indirizzo IP sia corretto e che il dev server sia in esecuzione.

.output/public è vuoto o mancante Assicurati di aver configurato nitro: { preset: 'static' } nella nuxt.config.ts e di eseguire bun run generate.

Risorse

Pronto a pubblicare la tua app? Scopri come Capgo può aiutarti a distribuire aggiornamenti più velocemente — registrati per un account gratuito oggi.

Continua con Costruisci un'app mobile Nuxt da zero con Capacitor 8

Se stai usando Crea un'app mobile Nuxt da zero con Capacitor 8 per pianificare l'automazione CI/CD e connetterla a CI/CD con Capgo per il workflow del prodotto in Capgo CI/CD, Capgo Native Build per il workflow di prodotto in Capgo Native Builds, Integrazioni Capgo per il workflow di prodotto in Integrazioni Capgo, Integrazione CI/CD per i dettagli implementativi in CI/CD Integration, e Integrazione GitHub Actions per i dettagli implementativi in GitHub Actions Integration.

Live updates per le app Capacitor

Quando un bug nel web-layer è live, rilascia la fix tramite Capgo invece di aspettare giorni l'approvazione degli store. Gli utenti ricevono l'update in background, mentre le modifiche native restano nel normale processo di review.

Ultimi articoli dal blog

Capgo ti offre gli insight migliori che ti servono per creare un'app mobile davvero professionale.