メインコンテンツにジャンプ
Tutorial

Build a Next.js Mobile App from Scratch with Capacitor 8

Step-by-step guide to creating a new Next.js 15 project and turning it into native iOS and Android mobile apps using Capacitor 8. Perfect for starting fresh with mobile-first development.

Martin Donadieu

Martin Donadieu

Content Marketer

Build a Next.js Mobile App from Scratch with Capacitor 8

Introduction

Want to build a mobile app with Next.js from the ground up? This guide walks you through creating a brand new Next.js 15 project configured for mobile from day one, then packaging it as native iOS and Android apps using Capacitor 8.

By the end of this tutorial, you’ll have a working mobile app running on simulators that you can continue developing and eventually publish to the App Store and Google Play.

所要時間: ~30分

作成するもの:

  • App Routerを使用するNext.js 15プロジェクト
  • モバイル用の静的エクスポート設定
  • Capacitor 8に必要なプラグイン
  • ネイティブiOSおよびAndroidアプリ
  • ライブリロード開発環境

既存のNext.jsアプリがある場合はこちらをご確認ください。 既存のNext.jsアプリをモバイルに変換する それに代わる。

前提条件

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

  • Node.js 18+ (確認する node --version)
  • Bun パッケージマネージャー(curl -fsSL https://bun.sh/install | bash)
  • Xcode (macOSのみ、iOS開発用)
  • Android Studio (Android開発用)

ステップ 1: 新しい Next.js プロジェクトを作成する

新しい Next.js 15 プロジェクトを作成してください:

bunx create-next-app@latest my-mobile-app

質問に答える際に、次のオプションを選択してください:

  • TypeScript: Yes (recommended)
  • ESLint: Yes
  • Tailwind CSS: Yes (recommended for mobile styling)
  • src/ directory: Yes
  • App Router: Yes (recommended)
  • Import alias: Default (@/*)

プロジェクトに移動してください:

cd my-mobile-app

ステップ 2: 静的エクスポート用に Next.js を設定する

Capacitor は静的 HTML/JS/CSS ファイルが必要です。静的エクスポート用に Next.js を設定するには、 next.config.ts:

import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  output: 'export',
  images: {
    unoptimized: true,
  },
  // Ensure trailing slashes for proper routing in Capacitor
  trailingSlash: true,
};

export default nextConfig;

これらの設定の理由は?

  • output: 'export' — Node.js サーバーが必要なくて静的 HTML を生成する
  • images: { unoptimized: true } — Next.js の画像最適化を無効にする (サーバーが必要)
  • trailingSlash: true — ネイティブ WebView で正しいルーティングを確保する

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

モバイル開発用のスクリプトを更新してください: package.json ビルドをテストしてください:

{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "mobile": "bun run build && bunx cap sync",
    "mobile:ios": "bun run mobile && bunx cap open ios",
    "mobile:android": "bun run mobile && bunx cap open android"
  }
}

ビルドが正常に動作していることを確認してください:

bun run build

Navigate to your project: is not translated as it is a protected token out 静的ファイルの保存場所。

ステップ 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 — キー値ストレージ (ローカルストレージと同じようにネイティブ)

Step 5: Capacitorを初期化

Capacitorをプロジェクトの詳細情報で初期化してください:

bunx cap init "My Mobile App" com.example.mymobileapp --web-dir out

置き換え:

  • "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: 'out',
  plugins: {
    SplashScreen: {
      launchShowDuration: 2000,
      launchAutoHide: true,
      androidScaleType: 'CENTER_CROP',
      splashFullScreen: true,
      splashImmersive: true,
    },
    Keyboard: {
      resize: 'body',
      resizeOnFullScreen: true,
    },
    StatusBar: {
      style: 'light',
    },
  },
};

export default config;

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

プラットフォーム パッケージをインストールしてください:

bun add @capacitor/ios @capacitor/android

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

bunx cap add ios
bunx cap add android

これにより ios ネイティブプロジェクトを含むディレクトリが作成されます。 android ステップ 7: ビルドと実行

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

iOS シミュレータで開く:

bun run mobile

または Android エミュレータで開く:

bun run mobile:ios

Xcode (iOS) で:

bun run mobile:android

デバイスドロップダウンからシミュレータを選択する

  1. プレイボタンをクリックするか
  2. Android Studio で: Cmd + R

In Android Studio:

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

ステップ8:ライブリロードの設定

開発のために、ライブリロードを有効にしてください。変更が即座にデバイスに表示されます。

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

# Windows
ipconfig
  1. Capacitorの開発設定を作成してください。追加する: capacitor.config.ts:
import type { CapacitorConfig } from '@capacitor/cli';

const devConfig: CapacitorConfig = {
  appId: 'com.example.mymobileapp',
  appName: 'My Mobile App',
  webDir: 'out',
  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: 'out',
  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で再構築してください

Next.jsのcodeの編集は、デバイスで即座にリロードされます。

ステップ9:最初のモバイルスクリーンを作成してください

簡単なモバイルフレンドリーなホームスクリーンを作成してください。更新してください。 src/app/page.tsx:

'use client';

import { useEffect, useState } from 'react';
import { App } from '@capacitor/app';
import { Keyboard } from '@capacitor/keyboard';

export default function Home() {
  const [appInfo, setAppInfo] = useState<{ name: string; version: string } | null>(null);

  useEffect(() => {
    // Get app info on mount
    App.getInfo().then(setAppInfo).catch(console.error);

    // Handle back button on Android
    const backHandler = App.addListener('backButton', ({ canGoBack }) => {
      if (!canGoBack) {
        App.exitApp();
      } else {
        window.history.back();
      }
    });

    // Hide keyboard when tapping outside inputs
    const keyboardHandler = Keyboard.addListener('keyboardWillShow', () => {
      document.body.classList.add('keyboard-open');
    });

    return () => {
      backHandler.then(h => h.remove());
      keyboardHandler.then(h => h.remove());
    };
  }, []);

  return (
    <main className="min-h-screen bg-linear-to-b from-blue-500 to-blue-700 flex flex-col items-center justify-center p-6 text-white">
      <h1 className="text-4xl font-bold mb-4">My Mobile App</h1>
      <p className="text-xl mb-8 text-center opacity-90">
        Built with Next.js 15 + Capacitor 8
      </p>

      {appInfo && (
        <div className="bg-white/20 rounded-lg p-4 backdrop-blur-sm">
          <p className="text-sm">
            {appInfo.name} v{appInfo.version}
          </p>
        </div>
      )}

      <div className="mt-12 space-y-4 w-full max-w-sm">
        <button className="w-full py-4 px-6 bg-white text-blue-600 rounded-xl font-semibold text-lg shadow-lg active:scale-95 transition-transform">
          Get Started
        </button>
        <button className="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">
          Learn More
        </button>
      </div>
    </main>
  );
}

ステップ 10: セーフ エリア ハンドリングを追加する

モバイル デバイスにはノッチ、ホーム インジケータ、ステータス バーがあります。Tailwind を使用してセーフ エリア ハンドリングを追加します。

更新 src/app/globals.css:

@tailwind base;
@tailwind components;
@tailwind utilities;

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

/* Keyboard handling */
.keyboard-open {
  --sab: 0px;
}

プロジェクト ストラクチャ

プロジェクトは次のようになります。

my-mobile-app/
├── android/              # Android native project
├── ios/                  # iOS native project
├── out/                  # Static build output
├── src/
│   ├── app/
│   │   ├── globals.css
│   │   ├── layout.tsx
│   │   └── page.tsx
│   └── ...
├── capacitor.config.ts   # Capacitor configuration
├── next.config.ts        # Next.js configuration
├── package.json
└── ...

次のステップ

Next.js モバイル アプリが正常に動作するようになりました。ここでは何をするかを説明します。

基本設定

  • アプリ アイコン: デフォルトのアイコンを ios/App/App/Assets.xcassetsandroid/app/src/main/res
  • スプラッシュ スクリーン: Customize in native projects or use @capacitor/splash-screen config
  • Deep Links: Configure URL schemes for your app

Add More Features

  • Camera: bun add @capacitor/camera
  • Geolocation: bun add @capacitor/geolocation
  • Push Notifications: bun add @capacitor/push-notifications
  • File System: bun add @capacitor/filesystem

Native UI and transitions

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

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

Tailwindのセーフエリアを追加するには @capgo/tailwind-capacitor:

bun add -D tailwind-capacitor

See Using @capgo/capacitor-native-navigation, Using @capgo/capacitor-transitionsそして tailwind-capacitor Next.js用の設定のために

tailwind-__CAPGO_KEEP_0__ リポジトリ

If content looks cropped, shifted, or horizontally scrollable on iOS, adding more overflow-x: hidden or tweaking the viewport tag alone usually does not fix it. Work through these checks in order.

Make sure the viewport meta tag is applied correctly

App Router (app/) export viewport from app/layout.tsx:

import type { Viewport } from 'next';

export const viewport: Viewport = {
  width: 'device-width',
  initialScale: 1,
  viewportFit: 'cover',
};

Pages Router (pages/) put the viewport meta tag in pages/_app.tsx, not _document.tsx.

iOS のセーフエリアを 1 つのルートラッパーからのみ取り扱う

Create a single app shell and apply safe area padding there — not in multiple nested components:

html,
body,
#__next {
  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);
}

すべてのページコンテンツを 1 つのラッパー内に包みます .app-shell. iOS の安全領域の余白をヘッダー、モーダル、レイアウトラッパーで重複して設定すると、UI が切り取られたり大きすぎるように見えることがよくあります。

@capgo/tailwind-capacitorで、同じ余白を表現できます。 pt-safe pb-safe px-safe 単一のシェルで

Set Capacitor iOS contentInset __CAPGO_KEEP_0__ never

最初 capacitor.config.tsIn contentInsetMode: 'css',

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

Mixing Capacitor’s automatic content inset with CSS env(safe-area-inset-*) 余白は、ダブルスペースの原因となる一般的な問題です。

実際にオーバーフローしている要素を探してください

通常の原因は、 100vwTailwind w-screen、固定ピクセル幅の要素、または大きい min-width.

Safari Web インスペクターで実行:

[...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安全エリアの余白の重複、または固定幅のコンテナ — ではなく、ビュー ポート メタタグ自体から

オーバー・ザ・エア更新

設定 Capgo アプリストアの再提出を回避するために更新をプッシュする:

bunx @capgo/cli init

トラブルシューティング

ビルドが「モジュールが見つかりません」というエラーで失敗する 実行 bun install 再度実行してみてください。

iOS: 「署名のアイデンティティが見つかりません」 Xcodeを開き、Signing &amp; Capabilitiesに移動し、開発チームを選択してください。

Android: 「SDK」の場所が見つかりません 作成 android/local.propertiessdk.dir=/path/to/android/sdk

デバイス上で表示されない変更 __CAPGO_KEEP_0__を実行してください bun run mobile ライブリロードの場合、IPアドレスが正しく、開発サーバーが実行されていることを確認してください。

リソース

アプリを配信する準備ができましたか? Capgo を使用して、更新を迅速に配信する方法を学びましょう — 無料アカウントに登録する 今日です。

Capacitor 8から始めて、Scratchから始まるNext.jsモバイルアプリを構築する

__CAPGO_KEEP_0__を使用している場合 Scratchから始まるNext.jsモバイルアプリを構築するCapacitor 8 CI/CD自動化の計画を行うには、__CAPGO_KEEP_0__ CI/CDに接続する Capgo CI/CDの製品ワークフロー Capgo Native Buildsの製品ワークフロー Capgo Integrationsの製品ワークフロー Capgo Integrations Capgo Integrations Capgo CI/CDのCI/CD Integrationの実装詳細 __CAPGO_KEEP_0__ CI/CDのCI/CD Integrationの実装詳細 __CAPGO_KEEP_0__ CI/CDのCI/CD Integrationの実装詳細 GitHub アクション統合 GitHub アクション統合の実装詳細について。

Capacitorアプリ用のリアルタイム更新

ウェブ層のバグが生じた場合、Capgoを使用して修正を配信し、App Storeの承認待ちの日数を待たずにユーザーにバックグラウンドで更新を提供する

スタートする

最新のブログ

Capgoは、プロフェッショナルなモバイルアプリを作成するために必要な最良の洞察を提供します。