Zum Hauptinhalt springen
Tutorials

Nuxt Mobile App von Grund auf bauen mit Capacitor 8

Schritt-für-Schritt-Anleitung: Erstelle ein neues Nuxt 4 Projekt und verwandle es mit Capacitor in native iOS- und Android-Apps. Ideal für Mobile-First Vue-Entwicklung.

Nuxt Mobile App von Grund auf bauen mit Capacitor 8

Einführung

Möchtest du eine mobile App mit Nuxt von Grund auf neu bauen? Dieser Guide zeigt dir, wie du ein brandneues Nuxt 4 Projekt erstellst, das von Tag eins an für Mobile konfiguriert ist, und es dann mit Capacitor 8.

Am Ende dieses Tutorials hast du eine funktionierende mobile App auf Simulatoren laufen, die du weiterentwickeln und schließlich im App Store und Google Play veröffentlichen kannst.

Zeitaufwand: ~30 Minuten

Das baust du:

  • Ein neues Nuxt 4 Projekt mit der neuesten Verzeichnisstruktur
  • Konfiguration der statischen Generierung für Mobile
  • Capacitor 8 mit essenziellen Plugins
  • Native iOS- und Android-Apps
  • Live-Reload-Setup für die Entwicklung

Schon eine Nuxt App? Schau dir an: Deine Nuxt App für Mobile konvertieren stattdessen.

Voraussetzungen

Stelle sicher, dass Folgendes installiert ist:

  • Node.js 18+ (prüfen mit node --version)
  • Bun Package Manager (curl -fsSL https://bun.sh/install | bash)
  • Xcode (nur macOS, für iOS-Entwicklung)
  • Android Studio (für Android-Entwicklung)

Schritt 1: Neues Nuxt 4 Projekt erstellen

Erstelle zuerst ein frisches Nuxt 4 Projekt:

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

Nuxt 4 Verzeichnisstruktur

Nuxt 4 nutzt eine neue Verzeichnisstruktur mit der App code in der app/ Verzeichnis:

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

Diese Struktur sorgt für eine bessere Trennung zwischen App- und Server-code.

Schritt 2: Nuxt für Static Generation konfigurieren

Capacitor benötigt statische HTML/JS/CSS-Dateien. Konfiguriere Nuxt für Static Generation in nuxt.config.ts:

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

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

Schritt 3: Mobile Scripts hinzufügen

Aktualisiere dein package.json mit Scripts für die mobile Entwicklung:

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

Static Generation testen:

bun run generate

Du solltest einen .output/public Verzeichnis mit deinen statischen Dateien.

Schritt 4: Capacitor 8 installieren

Installiere die Capacitor Core-Packages:

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

Installiere die wichtigsten Plugins für mobile Apps:

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

Was diese Plugins machen:

  • @capacitor/app — App-Lifecycle-Events (Foreground/Background, Deep Links)
  • @capacitor/keyboard — Tastaturverhalten steuern
  • @capacitor/splash-screen — Native Splash-Screen steuern
  • @capacitor/status-bar — Styling der Statusleiste des Geräts
  • @capacitor/preferences — Key-Value-Speicherung (wie localStorage, aber nativ)

Schritt 5: Capacitor initialisieren

Initialisiere Capacitor mit deinen Projektdaten:

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

Ersetze:

  • "My Mobile App" durch den Anzeigenamen deiner App
  • com.example.mymobileapp durch deine App-ID (Reverse-Domain-Notation)

Dadurch entstehen capacitor.config.ts. Aktualisiere es mit der Plugin-Konfiguration:

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;

Schritt 6: Native Plattformen hinzufügen

Installiere die Plattform-Packages:

bun add @capacitor/ios @capacitor/android

Native Projekte generieren:

bunx cap add ios
bunx cap add android

Dadurch entstehen ios und android Verzeichnisse mit den nativen Projekten.

Schritt 7: Build und Run

Baue dein Projekt und synchronisiere es mit den nativen Plattformen:

bun run mobile

Im iOS Simulator öffnen:

bun run mobile:ios

Oder Android Emulator:

bun run mobile:android

In Xcode (iOS):

  1. Wähle einen Simulator aus dem Geräte-Dropdown
  2. Klicke auf den Play-Button oder drücke Cmd + R

In Android Studio:

  1. Warten, bis Gradle das Syncing beendet hat
  2. Wähle einen Emulator aus dem Device-Dropdown
  3. Klicke auf den Run-Button oder drücke Shift + F10

Schritt 8: Live Reload einrichten

Aktiviere Live Reload für eine schnellere Entwicklung, damit Änderungen sofort auf deinem Gerät erscheinen.

  1. Ermitteln Sie Ihre lokale IP-Adresse:
# macOS
ipconfig getifaddr en0

# Windows
ipconfig
  1. Erstelle eine Capacitor Dev-Config. Aktualisieren 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. Starte den Dev-Server und kopiere die Config in den nativen Teil:
bun run dev &
NODE_ENV=development bunx cap copy
  1. In Xcode/Android Studio neu bauen

Änderungen an deinem Nuxt code werden nun per Hot-Reload auf dem Gerät angezeigt.

Schritt 9: Erstelle deinen ersten Mobile Screen

Lass uns einen mobil-optimierten Home Screen erstellen. Aktualisieren app/app.vue:

<template>
  <NuxtPage />
</template>

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

Schritt 10: Tailwind CSS hinzufügen

Damit das Styling funktioniert, füge Tailwind CSS zu deinem Projekt hinzu:

bun add tailwindcss @tailwindcss/vite

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

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

Schritt 11: Das Share Plugin hinzufügen

Implementieren wir die Share-Button-Funktionalität:

bun add @capacitor/share

Update app/pages/index.vue um das Share Plugin zu nutzen:

<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 und Rebuild:

bun run mobile

Projektstruktur

Dein Projekt sollte nun so aussehen:

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

Nächste Schritte

Deine Nuxt Mobile App läuft jetzt. Das sind die nächsten Schritte:

Grundlegendes Setup

  • App-Icons: Ersetze die Standard-Icons in ios/App/App/Assets.xcassets und android/app/src/main/res
  • Splash Screen: In nativen Projekten anpassen oder nutzen: @capacitor/splash-screen Konfiguration
  • Deep Links: URL-Schemes für deine App konfigurieren

Mehr Features hinzufügen

  • Kamera: bun add @capacitor/camera
  • Geolocation: bun add @capacitor/geolocation
  • Push-Benachrichtigungen: bun add @capacitor/push-notifications oder @capgo/capacitor-firebase-messaging für Firebase Cloud Messaging auf iOS und Android
  • Dateisystem: bun add @capacitor/filesystem

Native UI und Übergänge

Nutze Capgo Plugins statt Konsta UI für ein natives Mobile-Feeling:

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

Für Tailwind Safe Areas füge hinzu @capgo/tailwind-capacitor:

bun add -D tailwind-capacitor

Siehe Verwendung von @capgo/capacitor-native-navigation, Verwendung von @capgo/capacitor-transitions, und die tailwind-capacitor Repo für das Nuxt-spezifische Setup.

iOS-Layoutprobleme beheben (Viewport, Safe Area und horizontaler Overflow)

Wenn Inhalte auf iOS abgeschnitten, verschoben oder horizontal scrollbar wirken, hilft es meist nicht, mehr overflow-x: hidden hinzuzufügen oder nur das Viewport-Tag zu ändern. Gehe diese Checks der Reihe nach durch.

Stelle sicher, dass das Viewport-Meta-Tag korrekt angewendet wird

Unter nuxt.config.ts, setze den Viewport über app.head:

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

iOS Safe Area nur von einem Root-Wrapper aus handhaben

Erstelle eine einzige App-Shell und wende dort das Safe-Area-Padding an – nicht in mehreren verschachtelten Komponenten:

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

Umschließe den gesamten Seiteninhalt mit .app-shellDoppeltes Safe-Area-Padding in Headern, Modals und Layout-Wrappern lässt das UI oft abgeschnitten oder zu groß wirken.

Mit @capgo/tailwind-capacitor, kannst du dasselbe Padding mit Utilities wie pt-safe pb-safe px-safe in dieser einen Shell ausdrücken.

Setze Capacitor iOS contentInset zu never zuerst

Unter capacitor.config.ts, deaktiviere lieber native Insets und überlasse CSS (oder der Native Navigation contentInsetMode: 'css') die Safe Area:

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

Kombination von Capacitor’s automatischen Content Insets mit CSS env(safe-area-inset-*) Padding ist eine häufige Ursache für doppelte Abstände.

Das tatsächlich überlaufende Element finden

Der übliche Übeltäter ist ein Element mit 100vw, Tailwind w-screen, einer festen Pixelbreite oder einem großen min-width.

Im Safari Web Inspector ausführen:

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

Mit Tailwind ersetzen w-screen mit w-full wann immer möglich. Viele Probleme mit horizontalem Overflow entstehen durch 100vw / w-screen, doppeltes Safe-Area-Padding oder einen Container mit fester Breite – nicht durch das Viewport-Meta-Tag selbst.

Over-the-Air Updates

Einrichten Capgo um Updates ohne erneute Einreichung im App Store zu pushen:

bunx @capgo/cli init

Fehlerbehebung

Build schlägt fehl: „Cannot find module“ Ausführen bun install und versuche es erneut.

iOS: „No signing identity found“ Öffne Xcode, gehe zu Signing & Capabilities und wähle dein Development Team aus.

Android: „SDK location not found“ Erstellen android/local.properties mit sdk.dir=/path/to/android/sdk

Änderungen werden nicht auf dem Gerät angezeigt Stelle sicher, dass du ausgeführt hast: bun run mobile nach den Änderungen. Für Live Reload prüfe, ob die IP-Adresse stimmt und der Dev-Server läuft.

.output/public ist leer oder fehlt Make sure you configured nitro: { preset: 'static' } in nuxt.config.ts und führe aus: bun run generate.

Ressourcen

Bereit für den App-Release? Erfahre, wie Capgo dir hilft, Updates schneller auszuliefern — Registrieren Sie sich für ein kostenloses Konto noch heute.

Weiterlesen: Nuxt Mobile App von Grund auf mit Capacitor bauen 8

Wenn du es nutzt Nuxt Mobile App von Grund auf bauen mit Capacitor 8 um CI/CD-Automatisierung zu planen, verbinde es mit Capgo CI/CD für den Produkt-Workflow in Capgo CI/CD, Capgo Native Builds optimieren für den Produkt-Workflow in Capgo Native Builds, Capgo Integrationen für den Produkt-Workflow in Capgo Integrations, CI/CD-Integration für die Implementierungsdetails in der CI/CD-Integration und GitHub Actions-Integration für die Implementierungsdetails in der GitHub Actions-Integration.

Live-Updates für Capacitor-Apps

Wenn ein Bug im Web-Layer live ist, liefere den Fix über Capgo aus, statt Tage auf die App-Store-Freigabe zu warten. Nutzer bekommen das Update im Hintergrund, während native Änderungen im normalen Review-Prozess bleiben.

Neues aus unserem Blog

Capgo gibt dir die besten Insights, um eine wirklich professionelle Mobile App zu erstellen.