Allez directement au contenu principal
Guide de tutorat

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.

Martin Donadieu

Martin Donadieu

Spécialiste du contenu

Build a Nuxt Mobile App from Scratch with Capacitor 8

Introduction

Vous souhaitez créer une application mobile avec Nuxt à partir de zéro ? Ce guide vous guide à travers la création d'un nouveau projet Nuxt 4 configuré pour la mobilité dès le début, puis l'emballage sous forme d'applications natives iOS et Android à l'aide de Capacitor 8.

À la fin de ce tutorat, vous disposerez d'une application mobile fonctionnelle exécutée sur des simulateurs que vous pouvez continuer à développer et publier ultérieurement sur l'App Store et Google Play.

Durée requise : ~30 minutes

Ce que vous allez construire :

  • Un nouveau projet Nuxt 4 avec la structure de répertoire la plus récente
  • Configuration de génération statique pour les appareils mobiles
  • Capacitor 8 avec les plugins essentiels
  • Applications natives iOS et Android
  • Configuration de développement avec rechargement en direct

Déjà en possession d'une application Nuxt ? Consultez Convertir votre application Nuxt en application mobile au lieu de cela.

Prérequis

Assurez-vous d'avoir ces éléments installés :

  • Node.js 18+ (vérifiez avec node --version)
  • Bun gestionnaire de package (curl -fsSL https://bun.sh/install | bash)
  • Xcode (seulement sur macOS, pour le développement iOS)
  • Android Studio (pour le développement Android)

Étape 1 : Créez un nouveau projet Nuxt 4

Commencez par créer un projet Nuxt 4 frais :

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

Structure de répertoire de Nuxt 4

Nuxt 4 utilise une nouvelle structure de répertoire avec l'application code dans le app/ répertoire :

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

Cette structure fournit une meilleure séparation entre l'application et le serveur code.

Étape 2 : Configurer Nuxt pour la génération statique

Capacitor nécessite des fichiers HTML/JS/CSS statiques. Configurez Nuxt pour la génération statique dans nuxt.config.ts:

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

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

Étape 3 : Ajouter les scripts mobiles

Mettez à jour votre package.json avec les scripts de développement 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"
  }
}

Testez la génération statique :

bun run generate

Vous devriez voir un .output/public répertoire avec vos fichiers statiques.

Étape 4 : Installez Capacitor 8

Installez les packages de noyau Capacitor :

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

Installez les plugins essentiels dont la plupart des applications mobiles ont besoin :

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

Ce que font ces plugins :

  • @capacitor/app — Événements de cycle de vie de l'application (avant-plan/arrière-plan, liens profonds)
  • @capacitor/keyboard — Contrôle de l'écran de splash natif
  • @capacitor/splash-screen — Préférences
  • @capacitor/status-bar — Personnalisez la barre d'état du dispositif
  • @capacitor/preferences — Stockage clé-valeur (comme localStorage mais natif)

Étape 5 : Initialiser Capacitor

Initialisez Capacitor avec vos détails de projet :

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

Remplacez :

  • "My Mobile App" par le nom d'affichage de votre application
  • com.example.mymobileapp par l'ID de votre application (notation de domaine inversé)

Cela crée capacitor.config.tsMettez à jour avec la configuration du 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;

Étape 6 : Ajouter les plateformes natives

Installez les packages de plateforme :

bun add @capacitor/ios @capacitor/android

Générez les projets natifs :

bunx cap add ios
bunx cap add android

Cela crée ios et android répertoires contenant les projets natifs.

Étape 7 : Construire et Exécuter

Construire votre projet et synchroniser avec les plateformes natives :

bun run mobile

Ouvrir dans l'émulateur iOS :

bun run mobile:ios

Ou l'émulateur Android :

bun run mobile:android

Dans Xcode (iOS) :

  1. Sélectionnez un simulateur dans le menu des appareils
  2. Cliquez sur le bouton Jouer ou appuyez Cmd + R

Dans Android Studio :

  1. Attendez que Gradle termine la synchronisation
  2. Sélectionnez un émulateur dans le menu des appareils
  3. Appuyez sur le bouton Exécuter ou pressez Shift + F10

Étape 8 : Configurer la mise à jour en temps réel

Pour un développement plus rapide, activez la mise à jour en temps réel afin que les modifications apparaissent instantanément sur votre appareil.

  1. Trouvez votre adresse IP locale :
# macOS
ipconfig getifaddr en0

# Windows
ipconfig
  1. Create a development Capacitor config. Update 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. Démarrer le serveur de développement et copiez la configuration vers le natif :
bun run dev &
NODE_ENV=development bunx cap copy
  1. Rebâtissez dans Xcode/Android Studio

Maintenant, les modifications apportées à votre Nuxt code se reflèteront en temps réel sur l'appareil.

Étape 9 : Créez votre première écran mobile

Créez une page d'accueil mobile améliorée. Mettez à jour app/app.vue:

<template>
  <NuxtPage />
</template>

Créer 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>

Étape 10 : Ajoutez Tailwind CSS

Pour que le style fonctionne, ajoutez Tailwind CSS à votre projet :

bun add tailwindcss @tailwindcss/vite

Mise à jour 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()],
  },
});

Créer 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;
}

Étape 11 : Ajoutez le plugin de partage

Implémentons la fonctionnalité du bouton de partage :

bun add @capacitor/share

Mise à jour app/pages/index.vue pour utiliser le plugin de partage :

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

Synchronisez et reconstruisez :

bun run mobile

Structure du projet

Votre projet devrait ressembler à ceci :

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

Étapes suivantes

Vous avez maintenant une application mobile Nuxt fonctionnelle. Voici ce que vous devez faire ensuite :

Configuration de base

  • Icônes de l'application : Remplacez les icônes par défaut dans ios/App/App/Assets.xcassets et android/app/src/main/res
  • Écran de démarrage : Personnalisez dans les projets natifs ou utilisez @capacitor/splash-screen config
  • Liens profonds : Configurez les schémas de URL pour votre application

Ajoutez plus de fonctionnalités

  • Appareil photo : bun add @capacitor/camera
  • Localisation géographique : bun add @capacitor/geolocation
  • Notifications de push : bun add @capacitor/push-notifications ou @capgo/capacitor-firebase-messaging @__CAPGO_KEEP_0__/__CAPGO_KEEP_1__-firebase-messaging
  • pour Firebase Cloud Messaging sur iOS et Android bun add @capacitor/filesystem

Système de fichiers :

Use Capgo plugins instead of Konsta UI for a native mobile feel:

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

— transitions de page ressentant un sentiment natif @capgo/tailwind-capacitor:

bun add -D tailwind-capacitor

Voir Utiliser @capgo/capacitor-navigation-native, Utiliser @capgo/capacitor-transitions, et le repo tailwind-capacitor pour la configuration spécifique à Nuxt.

Résoudre les problèmes de mise en page iOS (Vueport, Zone de sécurité, et débordement horizontal)

Si le contenu semble coupé, décalé ou déroulable horizontalement sur iOS, ajouter plus overflow-x: hidden ou ajuster seul le tag Vueport ne résout généralement pas le problème. Effectuez ces vérifications dans l'ordre.

Vérifiez que le tag meta Vueport est appliqué correctement

En nuxt.config.tsConfigurez la vue par défaut app.head:

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

Gérez l'espace sûr iOS à partir d'un seul enveloppe racine

Créez une coquille d'application unique et appliquez-y une marge d'espace sûr, et non dans plusieurs composants imbriqués :

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

Enveloppez tout le contenu de la page à l'intérieur de .app-shellLa duplication de la marge d'espace sûr dans les en-têtes, les modales et les enveloppes de disposition rend souvent l'interface utilisateur coupée ou trop grande.

Avec @capgo/tailwind-capacitorvous pouvez exprimer la même marge avec des utilitaires comme pt-safe pb-safe px-safe sur cette coquille unique.

Configurez Capacitor iOS contentInset à never context : Page/zone : Page de mise à jour en direct. Rôle : Étiquette de l'interface utilisateur courte ou élément de navigation. Clé de message `live_update_dynamic_label_to` (Étiquette de mise à jour dynamique vers).

Dans capacitor.config.ts, préférez l'insérer natif désactivé et laissez CSS (ou la navigation native) contentInsetMode: 'css') posséder l'aire de sécurité :

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

Mélanger les Capacitor automatiques de contenu de l'insérer avec CSS env(safe-area-inset-*) la marge est une cause courante de double espace.

Trouvez l'élément réellement débordant

Le coupable habituel est un élément utilisant 100vw, Tailwind w-screen, une largeur de pixels fixe, ou une large min-width.

Dans l'inspecteur Web Safari, exécutez :

[...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,
  }));

Avec Tailwind, remplacez w-screen par w-full lorsque possible. De nombreux problèmes de débordement horizontal proviennent de 100vw / w-screenune mise en page de padding de zone de sécurité dupliquée, ou d'un conteneur de largeur fixe — et non de la balise meta de viewport elle-même.

Mises à jour hors ligne

Configurer Capgo pour envoyer des mises à jour sans résubmission de l'application sur l'app store :

bunx @capgo/cli init

Aide au dépannage

Échec de la construction avec « Impossible de trouver le module » Démarrez bun install et essayez à nouveau.

iOS : « Aucune identité de signature trouvée » Ouvrez Xcode, allez dans Signature et Capabilités, et sélectionnez votre équipe de développement.

Android : « Emplacement de SDK non trouvé » Créer android/local.properties avec sdk.dir=/path/to/android/sdk

Les modifications ne s'affichent pas sur le dispositif Vérifiez que vous avez exécuté bun run mobile après avoir apporté des modifications. Pour le rechargement en direct, vérifiez que l'adresse IP est correcte et que le serveur de développement est en cours d'exécution.

Le répertoire .output/public est vide ou manquant Vérifiez que vous avez configuré nitro: { preset: 'static' } dans nuxt.config.ts et exécutez bun run generate.

Ressources

Prêt à livrer votre application ? Découvrez comment Capgo peut vous aider à livrer des mises à jour plus rapidement — s'inscrire à un compte gratuit aujourd'hui.

Continuez de Build a Nuxt Mobile App from Scratch avec Capacitor 8

Si vous utilisez Build a Nuxt Mobile App from Scratch avec Capacitor 8 pour planifier l'automatisation de CI/CD, connectez-l’avec Capgo CI/CD pour le flux de travail du produit dans Capgo CI/CD, Capgo Builds natifs pour le flux de travail du produit dans Capgo Builds natifs, Capgo Intégrations pour le flux de travail du produit dans Capgo Intégrations, Intégration CI/CD pour le détail d'implémentation dans Intégration CI/CD, et GitHub Actions Intégration pour le détail d'implémentation dans GitHub Actions Intégration.

Live updates for Capacitor apps

When a web-layer bug is live, ship the fix through Capgo instead of waiting days for app store approval. Users get the update in the background while native changes stay in the normal review path.

Support humain de Martin

Démarrer maintenant

Dernières actualités de notre Blog

Capgo vous offre les meilleures informations nécessaires pour créer une application mobile véritablement professionnelle.