メインコンテンツへスキップ
チュートリアル

Capacitor 8 で Nuxt モバイルアプリをゼロから構築する

Capacitor を使用して、新しい Nuxt 4 プロジェクトの作成から iOS および Android の Native Build アプリへと変換するまでのステップバイステップガイドです。モバイルファーストの Vue 開発をゼロから始めるのに最適です。

Capacitor 8 で Nuxt モバイルアプリをゼロから構築する

はじめに

Nuxtでモバイルアプリをゼロから構築したいですか?このガイドでは、初日からモバイル向けに構成された新しいNuxt 4プロジェクトの作成から、Capacitorを使用してiOSおよびAndroidのNative Buildアプリとしてパッケージングするまでの手順を解説します。 Capacitor 8.

このチュートリアルを終える頃には、シミュレーターで動くモバイルアプリが完成し、そのまま開発を続けて最終的に App Store と Google Play に公開できます。

所要時間: 約30分

作成するもの:

  • 最新のディレクトリ構造を持つNuxt 4プロジェクト
  • モバイル向けの静的生成(SSG)設定
  • Capacitor 8(必須プラグイン付き)
  • iOSとAndroidのネイティブアプリ
  • Live reloadの開発環境

既存のNuxtアプリをお持ちですか?こちらをチェック Nuxtアプリをモバイル化する こちらを読んでください。

前提条件

以下のツールがインストールされていることを確認してください:

  • Node.js 18+ (確認: node --version)
  • Bun パッケージマネージャー(curl -fsSL https://bun.sh/install | bash)
  • iOSプロファイリング (macOSのみ、iOS開発用)
  • デバッグ (Android開発用)

ステップ 1: 新しい Nuxt 4 プロジェクトの作成

まずは、新しい Nuxt 4 プロジェクトを作成しましょう:

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

Nuxt 4 のディレクトリ構造

Nuxt 4では、app code を含む新しいディレクトリ構造が app/ 採用されています:

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

この構造により、アプリとサーバーの code がより明確に分離されます。

ステップ 2: Nuxt を静的生成用に設定する

Capacitor には静的な HTML/JS/CSS ファイルが必要です。Nuxt を静的生成用に設定し、 nuxt.config.ts:

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

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

ステップ 3: モバイルスクリプトを追加

以下のファイルを更新して、 package.json モバイル開発用のスクリプトを追加します:

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

静的生成をテストします:

bun run generate

以下の .output/public ディレクトリに静的ファイルが生成されているはずです。

ステップ 4: Capacitor 8 をインストール

Capacitor のコアパッケージをインストールします:

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

ほとんどのモバイルアプリに必要な必須プラグインをインストールします:

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

各プラグインの機能:

  • @capacitor/app — アプリのライフサイクルイベント (フォアグラウンド/バックグラウンド、ディープリンク)
  • @capacitor/keyboard — キーボードの挙動制御
  • @capacitor/splash-screen — ネイティブのスプラッシュ画面制御
  • @capacitor/status-bar — デバイスのステータスバーをスタイル設定
  • @capacitor/preferences — キーバリューストレージ(localStorage のようだがネイティブ)

ステップ 5: Capacitor を初期化

プロジェクトの詳細で Capacitor を初期化します:

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

以下を置き換え:

  • "My Mobile App" アプリの表示名に置き換え
  • com.example.mymobileapp アプリID(リバースドメイン形式)に置き換え

これにより以下が作成されます: capacitor.config.ts. プラグイン設定で更新します:

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;

ステップ 6: ネイティブプラットフォームを追加

プラットフォームパッケージをインストールします:

bun add @capacitor/ios @capacitor/android

ネイティブプロジェクトを生成します:

bunx cap add ios
bunx cap add android

これにより以下が作成されます: ios と android ネイティブプロジェクトが含まれるディレクトリ。

ステップ 7: ビルドと実行

プロジェクトをビルドしてネイティブプラットフォームと同期します:

bun run mobile

iOS Simulatorで開く:

bun run mobile:ios

またはAndroid Emulator:

bun run mobile:android

Xcode (iOS) で:

  1. デバイスのドロップダウンからシミュレータを選択
  2. 再生ボタンをクリックするか、以下を押します Cmd + R

Android Studio で:

  1. Gradleの同期が完了するまで待機
  2. デバイスのドロップダウンからエミュレータを選択
  3. Runボタンをクリックするか、以下を押してください Shift + F10

ステップ 8: Live Reload の設定

開発を効率化するには、live reloadを有効にしてください。変更がデバイスに即座に反映されます。

  1. ローカルIPアドレスを確認してください:
# macOS
ipconfig getifaddr en0

# Windows
ipconfig
  1. 開発用の Capacitor 設定を作成します。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. 開発サーバーを起動し、設定をネイティブにコピーします:
bun run dev &
NODE_ENV=development bunx cap copy
  1. Xcode/Android Studioで再ビルド

これで、Nuxtの code を編集すると、デバイス上でホットリロードされるようになります。

ステップ 9: 最初のモバイル画面を作成

モバイルフレンドリーなホーム画面を作成しましょう。Update app/app.vue:

<template>
  <NuxtPage />
</template>

作成します 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>

ステップ 10: Tailwind CSSを追加

スタイリングを適用するために、プロジェクトに Tailwind CSS を追加します:

bun add tailwindcss @tailwindcss/vite

アップデート 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()],
  },
});

作成します 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;
}

ステップ 11: Share プラグインの追加

シェアボタンの機能を実装しましょう:

bun add @capacitor/share

アップデート app/pages/index.vue 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>

同期と再ビルド:

bun run mobile

プロジェクト構成

プロジェクトは現在、以下のようになっているはずです:

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

次のステップ

Nuxt モバイルアプリが完成しました。次のステップは以下の通りです:

必須セットアップ

  • アプリのアイコン: デフォルトのアイコンを以下で置き換えます: ios/App/App/Assets.xcassets と android/app/src/main/res
  • スプラッシュ画面: ネイティブプロジェクトでカスタマイズするか、以下を使用します: @capacitor/splash-screen config
  • ディープリンク: アプリのURLスキームを設定します:

さらに機能を追加

  • カメラ: bun add @capacitor/camera
  • 位置情報: bun add @capacitor/geolocation
  • プッシュ通知: bun add @capacitor/push-notifications または @capgo/capacitor-firebase-messaging iOSおよびAndroid向けのFirebase Cloud Messaging用
  • ファイルシステム: bun add @capacitor/filesystem

ネイティブUIとトランジション

ネイティブなモバイル体験を実現するために、Konsta UIの代わりにCapgoプラグインを使用します:

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

Tailwindのセーフエリア設定には、以下を追加します @capgo/tailwind-capacitor:

bun add -D tailwind-capacitor

詳細は @capgo/capacitor-native-navigation の活用, @capgo/capacitor-transitions の活用、および tailwind-capacitor リポジトリ Nuxt専用のセットアップ用。

iOSのレイアウト問題を修正(ビューポート、セーフエリア、横方向のオーバーフロー)

iOSでコンテンツが切れて見えたり、ずれて見えたり、横スクロールが発生したりする場合は、さらに追加するだけでも、 overflow-x: hidden viewportタグを調整するだけでも通常は解決しません。以下のチェックを順番に確認してください。

viewport メタタグが正しく適用されているか確認する

では nuxt.config.ts、ビューポートを以下で設定します: app.head:

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

iOSのセーフエリアは1つのルートラッパーだけで処理する

単一のアプリシェルを作成し、そこにセーフエリアのパディングを適用してください — 複数のネストしたコンポーネントではなく:

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

すべてのページコンテンツを以下でラップします: .app-shellヘッダー、モーダル、レイアウトラッパーで safe-area のパディングが重複すると、UI が欠けたり、逆に大きすぎたりすることがよくあります。

を使用すると、 @capgo/tailwind-capacitor同じパディングを次のようなユーティリティで表現できます: pt-safe pb-safe px-safe その単一のシェル上で。

Capacitor iOS を設定 contentInset まで never 最初に

では capacitor.config.ts、native inset を無効にすることを推奨し、CSS(または Native Navigation のもの contentInsetMode: 'css')に safe area を任せる:

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

Capacitor の自動コンテンツ inset と CSS を混在させると env(safe-area-inset-*) 余白(padding)が原因で、二重にスペースが開いてしまうことがよくあります。

実際にオーバーフローしている要素を特定する

主な原因は、以下の要素を使用している場合です: 100vwTailwind CSS、 w-screen固定ピクセル幅、または大きな min-width.

Safari Web Inspectorで以下を実行してください:

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

Tailwindを使用する場合は、可能な限り w-screen を使用して w-full 置き換えてください。水平方向のオーバーフロー問題の多くは 100vw / w-screen、重複したセーフエリアのパディング、または固定幅のコンテナに起因し、viewportのmetaタグ自体が原因ではありません。

Over-the-Air アップデート

セットアップ Capgo App Storeへの再申請なしでアップデートを配信するには:

bunx @capgo/cli init

トラブルシューティング

ビルドエラー:「Cannot find module」が発生 実行 bun install して、もう一度試してください。

iOS: 「No signing identity found」 Xcodeを開き、Signing & Capabilitiesに移動して、開発チームを選択してください。

Android: “SDK の場所が見つかりません” 作成します android/local.properties を使用して sdk.dir=/path/to/android/sdk

デバイスに変更が反映されない 変更を加えた後は、必ず以下を実行してください: bun run mobile ライブリロードを使用する場合は、IPアドレスが正しいことと、開発サーバーが起動していることを確認してください。

.output/public が空、または存在しない 設定が完了しているか確認してください nitro: { preset: 'static' } における nuxt.config.ts を実行します bun run generate.

リソース

アプリをリリースする準備はできましたか?Capgo を使って、より素早くアップデートを配信する方法を学びましょう — 無料アカウントに登録する を今すぐ作成してください。

Capacitor で Nuxt モバイルアプリをゼロから構築する 8 関連記事

使用している場合 Capacitor 8 で Nuxt モバイルアプリをゼロから構築する CI/CD自動化を計画するには、次と連携してください Capgo での CI/CD 設定 Capgo CI/CDでのプロダクトワークフローについては、 Capgo Native Build Capgo Native Buildsでのプロダクトワークフローについては、 Capgo インテグレーション Capgo インテグレーションでのプロダクトワークフロー向けに、 CI/CD 連携 CI/CD Integrationの実装詳細については、 GitHub Actions 連携 GitHub Actions Integrationの実装詳細については。

Capacitor アプリ向けの Live Updates

Web レイヤーのバグが本番で発生したら、App Store の審査を何日も待たずに Capgo で修正を配信しましょう。ネイティブの変更は通常の審査フローに残したまま、ユーザーはバックグラウンドでアップデートを受け取れます。

最新のブログ記事

Capgoは、真にプロフェッショナルなモバイルアプリを作るために必要な最高のインサイトを提供します。