framework-to-capacitor について
Next.js、Nuxt、Vite などのプロジェクトに Capacitor を導入するための開発スキルです。静的アセットの生成とビルドから同期までの設定に加え、ルーティングやディープリンク、サーバー専用機能の問題に対応します。
主な機能
- 01フレームワークの静的ビルド設定
- 02Capacitor の webDir 設定
- 03ビルドと cap sync の連携
- 04ルーティングとディープリンクの調査
- 05サーバー専用機能の特定
活用シーン
- 01index.html 不足による cap sync エラーの修正
- 02フレームワーク出力のモバイルアプリ向け準備
- 03再読み込み後の白画面やルート誤りの調査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ cap-go/ capgo-skills --skill framework-to-capacitorインストール後、Claude Code で/framework-to-capacitorを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
metadata.jsonreferences/angular.mdnextjs.mdother-frameworks.mdreact.mdsvelte.mdvue-nuxt.md詳細
フレームワークを Capacitor に対応させる
フレームワークから、Capacitor がネイティブプロジェクトにコピーできる静的バンドルを出力し、スクリプトを設定して build -> cap sync を 1 ステップで実行できるようにします。
使用するケース
次の場合に使用します。
- Next.js、Nuxt、Angular、SvelteKit、Vite(React/Vue/Svelte/Solid)、React Router、Qwik、または Astro のプロジェクトに Capacitor を追加するとき。
npx cap syncがThe web assets directory (./dist) must contain an index.html file.またはCould not find the web assets directory: ./dist.というエラーで失敗するとき。- アプリの起動時に白い画面が表示される、または再読み込みやディープリンク後に誤ったページが読み込まれるとき。
- SSR 専用機能(
output: 'export'エラー、prerenderエラー、API ルート、ミドルウェア、サーバーアクション)が原因でフレームワークのビルドが失敗するとき。
次の場合は使用しないでください。
- ストアへの申請準備、ネイティブ UX、権限、課金、審査のリスクについては、
webapp-to-capacitor、capacitor-app-store、capacitor-apple-review-preflightを使用してください。 - Cordova/PhoneGap アプリについては、
cordova-to-capacitorを使用してください。 - Capacitor のバージョンアップについては、
capacitor-app-upgradesを使用してください。 - ライブアップデートの設定については、
capgo-live-updatesを使用してください。
ライブプロジェクトのスナップショット
検出されたフレームワークとビルドの依存関係:
!node -e "const fs=require('fs');if(!fs.existsSync('package.json'))process.exit(0);const pkg=JSON.parse(fs.readFileSync('package.json','utf8'));const matchers=['next','nuxt','react','react-router','@react-router/dev','vue','@angular/core','@angular/build','@sveltejs/kit','@sveltejs/adapter-static','@builder.io/qwik','@qwik.dev/core','astro','@remix-run/react','solid-js','@solidjs/start','vite','@capacitor/core','@capacitor/cli'];const out=[];for(const section of ['dependencies','devDependencies']){for(const [name,version] of Object.entries(pkg[section]||{})){if(matchers.includes(name))out.push(section+'.'+name+'='+version)}}for(const [name,cmd] of Object.entries(pkg.scripts||{})){if(['build','export','generate','sync','cap:sync'].includes(name))out.push('scripts.'+name+'='+cmd)}console.log(out.join('\n'))"
関連するフレームワークと Capacitor の設定ファイルのパス:
!find . -maxdepth 3 -not -path '*/node_modules/*' \( -name 'next.config.*' -o -name 'nuxt.config.*' -o -name 'vite.config.*' -o -name 'angular.json' -o -name 'svelte.config.*' -o -name 'react-router.config.*' -o -name 'astro.config.*' -o -name 'capacitor.config.json' -o -name 'capacitor.config.ts' -o -name 'capacitor.config.js' \)
基本情報(すべてのフレームワークに適用)
- Capacitor は
webDirをネイティブアプリにコピーします。Node は実行しません。SSR、API ルート、ミドルウェア、ISR、サーバーアクション、cookies()/headers()、画像最適化サーバーは実行時に存在しません。これらはホストされたバックエンドに移し、HTTPS 経由で呼び出してください。 - アプリは
capacitor://localhost(iOS)およびhttps://localhost(Android)から配信されます。バックエンドでこれらのオリジンからの CORS を許可するか、CapacitorHttpを使用してください。 - Capacitor のローカルサーバーは、拡張子のないパスに対してルートの
index.htmlを返します。履歴モードの SPA ルーティング(createWebHistory、BrowserRouter、Angular のデフォルトのPathLocationStrategy)は動作します。ハッシュルーティングは不要です。 - 同じフォールバックにより、ルートごとの HTML ファイル(Next.js のエクスポート、SvelteKit の事前レンダリング、Astro)は、拡張子のない URL では配信されません。コールドスタート時や
/settings/でlocation.reload()を実行したときは、ルートのindex.htmlが読み込まれます。ナビゲーションはクライアント側で行い、App.addListener('appUrlOpen', ...)とルーターを使ってディープリンクを処理してください。 - アセットのパス:フレームワークのベースは
/のままにしてください(Vite のbase、Angular の<base href="/">、Next のbasePath/assetPrefixは未設定にします)。Web ホスティングで使う CDN やサブパスのベースは、アプリでのアセット読み込みを妨げます。モバイル用には別のビルド設定を使用してください。 - 環境変数はビルド時に埋め込まれます(Next.js の
NEXT_PUBLIC_*、Vite のVITE_*、Nuxt のNUXT_PUBLIC_*、SvelteKit のPUBLIC_*、Angular のenvironment.ts)。cap syncの前には毎回ビルドし、本番ビルドでlocalhostを指定しないでください。 - Capacitor 8 には Node 22 以降が必要です。Capacitor 9(現在は
@nextで、まだ GA ではありません)には Node 24 以降が必要です。フレームワークのメジャーバージョンによっては、Node の最低要件も引き上げられます(SvelteKit 3:Node 22.17 以降)。
手順
- スナップショットを読み取ります。フレームワーク、メジャーバージョン、現在のビルド出力ディレクトリを特定します。モノレポに複数のアプリがある場合は、どのアプリをモバイル向けにリリースするのか確認します。
- 該当するリファレンスのみを読み込みます(対象のものだけ)。
| package.json のフレームワーク | 読み込むファイル | webDir |
|---|---|---|
next | references/nextjs.md | out |
nuxt | references/vue-nuxt.md | .output/public |
vue + vite(Nuxt なし) | references/vue-nuxt.md | dist |
react + vite、react-router/@react-router/dev、CRA | references/react.md | dist、build/client、または build |
@angular/core | references/angular.md | dist/<project>/browser |
@sveltejs/kit または svelte + vite | references/svelte.md | build または dist |
solid-js、@builder.io/qwik/@qwik.dev/core、astro、@remix-run/* | references/other-frameworks.md | 可変 |
- 設定を変更する前に、サーバー専用機能を洗い出します(
app/api/、pages/api/、middleware.ts/proxy.ts、'use server'、getServerSideProps、+page.server.ts、+server.ts、server/api/、@angular/ssrを grep します)。一覧をユーザーに報告し、削除する前に、それぞれをどこに移行するか(外部 API、クライアント側の fetch、またはネイティブプラグイン)について合意します。 - 静的ビルドを動作させ、出力ディレクトリを確認します。ルートに
index.htmlが含まれている必要があります。 - Capacitor をインストールして初期化します(インストールにはリポジトリのパッケージマネージャーを使用します)。
npm install @capacitor/core @capacitor/ios @capacitor/android
npm install -D @capacitor/cli
npx cap init "App Name" com.company.app --web-dir <webDir>
npx cap add ios
npx cap add android
- 同期前に必ずビルドするスクリプトを1つ追加します。例:
"build:mobile": "<framework build> && cap sync"。 - 以下の検証を両方のプラットフォームで実行します。
最小限の設定(androidScheme: 'https' は Capacitor 6 以降のデフォルトです。以前の origin を維持する場合にのみ設定します)。
import type { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'com.company.app',
appName: 'App Name',
webDir: 'dist',
};
export default config;
ライブリロード
Capacitor 8:npx cap run ios -l --host <LAN-IP> --port <port>。Capacitor 9:npx cap run ios --url http://<LAN-IP>:<port>(-l/--host/--port/--https フラグは --url に統合されました)。開発サーバーは LAN でリッスンする必要があります(Vite の場合は --host 0.0.0.0 / server.host: true)。server.url を capacitor.config.* にコミットしないでください。
検証
npm run build
test -f <webDir>/index.html && echo "index.html OK"
npx cap sync
npx cap run ios # or open in Xcode
npx cap run android
次に、デバイス/シミュレーターで以下を確認します。
- アプリが、スプラッシュ画面以外で白い画面が表示されることなく、最初の画面を開くこと。
- ネストされたルートに移動し、バックグラウンド化してから復帰しても、ルートが維持されること。
- ディープリンク(
xcrun simctl openurl booted <url>/adb shell am start -a android.intent.action.VIEW -d <url>)を開き、正しい画面に遷移すること。 - WebView(Safari Web Inspector /
chrome://inspect)を調べ、JS/CSS の 404 と CORS エラーがないこと。 grep -rn "localhost:\|127.0.0.1" <webDir>で予期しない結果が返らないこと。
エラー処理
| 症状/エラー | 原因 | 対処方法 |
|---|---|---|
The web assets directory (./dist) must contain an index.html file. | webDir が誤ったフォルダーを指しているか、SSR ビルドに index.html がありません | webDir を上記の静的出力先に設定し、静的/SPA モードを有効にします |
Could not find the web assets directory: ./out. | ビルドが実行されていないか、出力先ディレクトリが異なります | 先にビルドを実行し、webDir を実際のフォルダーに合わせます |
白い画面が表示され、コンソールに /_next/... または /assets/... の Failed to load resource が表示される | ルート以外のベースパス/CDN の assetPrefix | モバイルビルドではベースパスに / を使用します |
| 白い画面が表示され、404 は発生しない | 起動時の JS エラー(プリレンダリング中の window/document や、SSR 専用のインポートがよくある原因です) | WebView のコンソールを確認し、ブラウザー専用コードにガードを追加します |
| 再読み込みまたはディープリンクでホームページが開く、あるいはハイドレーションエラーが発生する | Capacitor は拡張子のないパスに対してルートの index.html を返します | クライアントサイドナビゲーションと appUrlOpen ハンドラーを使用します。フレームワークのリファレンスを参照してください |
Page "/x/[id]" is missing "generateStaticParams()"(Next.js) | エクスポートモードでの動的ルート | パラメーターを事前生成するか、クエリ文字列/クライアントルートに切り替えます |
Image Optimization using the default loader is not compatible with output: export | next/image のデフォルトローダー | images.unoptimized: true またはカスタムローダーを使用します |
| API 呼び出しがブラウザーでは動作するが、デバイスでは失敗する | capacitor://localhost/https://localhost に対する CORS、または localhost の URL が埋め込まれています | バックエンドでオリジンを許可するか、CapacitorHttp を有効にします。環境変数を修正します |
リファレンス
references/nextjs.md- Next.js 14-16 の静的エクスポート、App Router の落とし穴、動的ルート、画像。references/react.md- Vite React、React Router フレームワークの SPA モード、CRA のレガシー版。references/vue-nuxt.md- Vue + Vite、Nuxt 4 の SPA 生成。references/angular.md- Angular 17+ のアプリケーションビルダー出力、SSR の削除。references/svelte.md- adapter-static を使用した SvelteKit 2 および 3、Svelte + Vite 単体。references/other-frameworks.md- Solid、Qwik、Astro、Remix v2。
関連スキル:webapp-to-capacitor(ストア公開準備)、capacitor-deep-linking、safe-area-handling、capgo-live-updates。