メインコンテンツへスキップ

Capacitor と Electron でデスクトップアプリをビルドする (2026)

Capacitor アプリを Electron でデスクトップアプリ化: セキュアなシェル設定、プラグイン戦略、macOS/Windows/Linux 向けパッケージング、Live Update。

Capgo バナー: Capacitor プロジェクトから Electron でデスクトップアプリを構築するガイド

Capacitor アプリからデスクトップアプリを構築するには、同じ Web ビルド (webDir) を Electron シェルにラップします。ウィンドウを開くメインプロセスが BrowserWindow, 小さなデスクトップ用 API を公開する preload スクリプト、そして macOS、Windows、Linux 用のインストーラーを生成する electron-builder を使います。UI とビジネスロジックは iOS や Android と共通のまま使え、デスクトップ専用の機能は Electron のメインプロセスに置きます。

このガイドでは、2026 年時点の Electron 上の Capacitor の現状、Capacitor 8 で動くセキュアなセットアップ、プラグインの挙動、パッケージングと署名、そして新しいインストーラーなしで Web アップデートを配信する方法を解説します。

2026 年時点の Electron 上の Capacitor の現状

長年、定番の答えは @capacitor-community/electron, でした。これは Electron をプラットフォームとして Capacitor CLI に接続するものでした。しかし、そのプロジェクトは自身の README でメンテナンスされていないと明記されています。最後の npm リリースとなる 5.0.1 は 2023 年 9 月公開で、Capacitor 5.4 以降を必要とし、Electron 26 を対象にビルドされています。それ以降 Electron は多くのメジャーバージョンをリリースしており、セキュリティ修正が入るのは最新 3 メジャーのみです。

古いプロジェクトでは引き続き使えますが、Capacitor 8 のアプリでは比較するとこうなります:

アプローチ メリット デメリット
自前の Electron シェル(本ガイド) 最新の Electron、完全なコントロール、余計な抽象化なし メインと preload の code を約 60 行書くだけ
@capacitor-community/electron 使い慣れた cap コマンド メンテナンスされておらず、古い Capacitor と Electron に固定されている

シェル方式は、Capacitor がすでに静的な Web ビルドを生成しているため、思ったより手間がかかりません。Electron はそれをロードするだけで済みます。

Electron でのプラグインの動作

Electron のレンダラーは Chromium なので、Capacitor アプリはブラウザと同じように動作します。Capacitor のコアはネイティブブリッジを検知せず、各プラグインの Web 実装にフォールバックします。

プラグインの種類 Electron で動作する? 対処方法
Web 実装を持つプラグイン(Preferences、Share、Clipboard、多くの Capgo プラグイン) はい、Web 版を使用します Web での動作をテストする
ネイティブ専用プラグイン(Web実装なし) いいえ、未実装として拒否されます preload API の背後にElectron相当のコードを書く
純粋なJSライブラリ はい なし

Capacitor.getPlatform() を返します 'web' このセットアップでは、 Capacitor.isNativePlatform() を返します false。preload スクリプトから明示的なデスクトップフラグを公開し、code がクリーンに分岐できるようにします。

前提条件

  • 静的フォルダを出力する Capacitor アプリの bun run build ( webDir における capacitor.config.ts、多くの場合 dist または www).
  • Bun と最新の LTS 版 Node.js。Electron ツールは依然として Node で動作するためです。
  • macOS の配布には Apple Developer ID 証明書、Windows には code 署名証明書が必要です。

ステップ 1: Electron と packager をインストールする

bun add -d electron electron-builder

プロジェクトのルートに electron/ フォルダを追加します:

my-app/
  capacitor.config.ts
  dist/              # web build, shared with iOS and Android
  electron/
    main.ts
    preload.ts
  electron-builder.yml

ステップ 2: Web ビルドがディスクから読み込まれるようにする

Electron は本番環境で file:// プロトコルを使ってファイルを読み込みます。2つの設定が重要です:

  1. アセットの相対パス。 Vite を使用する場合、 base: './' を設定し、ビルドされた index.html が ./assets/... の代わりに /assets/....
  2. ルーティング。 ディープリンクなど /settings ファイルとして存在しません。デスクトップビルドではハッシュルーティングを使用するか、読み込み後にクライアントサイドでのみナビゲーションを行うようルーターを設定してください。 index.html.
// vite.config.ts
import { defineConfig } from 'vite';

export default defineConfig({
  base: './',
});

相対パスは iOS と Android でも動作するため、別のビルドは不要です。

ステップ 3: メインプロセスを書く

// electron/main.ts
import { app, BrowserWindow, ipcMain, shell } from 'electron';
import path from 'node:path';

const isDev = !app.isPackaged;

async function createWindow() {
  const win = new BrowserWindow({
    width: 1280,
    height: 800,
    minWidth: 800,
    minHeight: 600,
    show: false,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true,
      nodeIntegration: false,
      sandbox: true,
    },
  });

  win.once('ready-to-show', () => win.show());

  // Open external links in the default browser, never inside the app.
  // Only allow https: so a script cannot launch file: or other protocol handlers.
  win.webContents.setWindowOpenHandler(({ url }) => {
    try {
      if (new URL(url).protocol === 'https:') void shell.openExternal(url).catch(() => {});
    } catch {
      // invalid URL: ignore
    }
    return { action: 'deny' };
  });

  if (isDev) {
    await win.loadURL('http://localhost:5173');
    win.webContents.openDevTools({ mode: 'detach' });
  } else {
    await win.loadFile(path.join(__dirname, '..', '..', 'dist', 'index.html'));
  }
}

ipcMain.handle('desktop:get-version', () => app.getVersion());

app.whenReady().then(createWindow);

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit();
});

app.on('activate', () => {
  if (BrowserWindow.getAllWindows().length === 0) createWindow();
});

保持 contextIsolation: true, nodeIntegration: falseや sandbox: true。Web アプリはサードパーティのスクリプトを読み込む可能性がありますが、それらに Node.js へのアクセスを許可すべきではありません。

ステップ 4: preload から小さなデスクトップ API を公開

// electron/preload.ts
import { contextBridge, ipcRenderer } from 'electron';

contextBridge.exposeInMainWorld('desktop', {
  isElectron: true,
  platform: process.platform,
  getVersion: () => ipcRenderer.invoke('desktop:get-version') as Promise<string>,
});

Web code に記述し、プラグインと同様に使用します:

// src/desktop.ts
declare global {
  interface Window {
    desktop?: {
      isElectron: boolean;
      platform: string;
      getVersion: () => Promise<string>;
    };
  }
}

export const isDesktop = () => Boolean(window.desktop?.isElectron);

デスクトップで必要なネイティブ機能はそれぞれ 1 つの ipcMain.handle メインプロセスと、preload 内の 1 つの関数になります。レンダラーは信頼できないため、引数の検証はメインプロセスで行ってください。

ステップ 5: ビルドして実行

Electron ファイルを Bun でコンパイルします。preload はサンドボックス内で実行されるため、単一の CommonJS ファイルとしてバンドルしてください:

{
  "main": "electron/dist/main.js",
  "scripts": {
    "build": "vite build",
    "electron:compile": "bun build electron/main.ts --outdir electron/dist --target node --format cjs --external electron && bun build electron/preload.ts --outdir electron/dist --target node --format cjs --external electron",
    "electron:dev": "bun run electron:compile && electron .",
    "electron:pack": "bun run build && bun run electron:compile && electron-builder"
  }
}

開発中は、1つのターミナルで開発サーバーを起動します(bun run dev)、離れるときは bun run electron:dev 別のターミナルで実行。デスクトップウィンドウでホットリロードが使えます。

ステップ 6: electron-builder でインストーラーをパッケージ化

# electron-builder.yml
appId: com.example.myapp
productName: My App
directories:
  output: release
files:
  - dist/**/*
  - electron/dist/**/*
  - package.json
mac:
  target: [dmg, zip]
  category: public.app-category.productivity
  hardenedRuntime: true
win:
  target: [nsis]
linux:
  target: [AppImage, deb]
  category: Utility

実行 bun run electron:packelectron-builder は実行している OS 向けにビルドします。CI で macOS、Windows、Linux のランナーをマトリックスで回して、3 つすべてを生成しましょう。

OS 主なターゲット 署名
macOS .dmg, .zip Developer ID 証明書と公証がないと、Gatekeeper にアプリをブロックされます
Windows NSIS .exe、MSI SmartScreen の警告を回避するには Code 署名証明書を使用してください
Linux AppImage、 .deb, .rpm 任意

Electron アプリは Chromium と Node.js を同梱するため、モバイルビルドよりもサイズが大きくなります。ターゲットによって異なりますが、インストーラーは 80〜150 MB 程度を想定してください。

ステップ 7: 新しいインストーラーなしでWebアップデートを配信

Capacitor アプリの変更の多くは Web 側の変更です。モバイルではこれらを Capgo Live Updatesで配信できます。デスクトップでは、 @capgo/electron-updater が同じモデル(チャネル、組み込みバンドル、ダウンロード済みバンドル、新バンドルが呼び出さない場合の自動ロールバック)で同じ役割を果たします。 notifyAppReady().

bun add @capgo/electron-updater

メインプロセスを更新し、アップデータが読み込むバンドルを選択できるようにします:

// electron/main.ts (production branch)
import { ElectronUpdater, setupIPCHandlers, setupEventForwarding } from '@capgo/electron-updater';

const updater = new ElectronUpdater({
  appId: 'com.example.myapp',
  autoUpdate: true,
});

// inside createWindow(), instead of loading dist/index.html directly
const builtinPath = path.join(__dirname, '..', '..', 'dist', 'index.html');
await updater.initialize(win, builtinPath);
setupIPCHandlers(updater);
setupEventForwarding(updater, win);
await win.loadFile(updater.getCurrentBundlePath());

同じ preload ファイルにアップデータブリッジを追加します:

// electron/preload.ts (next to the desktop API)
import { exposeUpdaterAPI } from '@capgo/electron-updater/preload';

exposeUpdaterAPI();

デスクトップ環境でのみ、Web アプリからの正常な起動を確認します:

import { isDesktop } from './desktop';

if (isDesktop()) {
  const { requireUpdater } = await import('@capgo/electron-updater/renderer');
  await requireUpdater().notifyAppReady();
}

モバイルで使用するのと同じ CLI で新しいバンドルをアップロードします:

bun run build
bunx @capgo/cli@latest bundle upload --channel=production

Web 更新では Electron 本体、メインプロセス、preload は変更できません。それらには、バイナリ自動更新機能付きの新しいインストーラーを配布してください。当社のガイドでは Electronの自動アップデート そのパスをカバーしており、 electron-updater プラグインのページ と ドキュメント すべてのオプションを網羅しています。

トラブルシューティング

パッケージ化されたアプリでウィンドウが真っ白になる。 アセットが絶対パスを使用しています。設定して base: './' 再ビルドしてください。パッケージ化されたアプリで DevTools を開き、404 を確認します。 file:///assets/....

リロード後にルートが空白ページになる。 ルーターが読み込もうとしました /route ディスクから。デスクトップ向けにはハッシュルーティングに切り替えてください。

require is not defined またはプリロードエラー。 サンドボックス化されたプリロードは、実行時に任意のパッケージをロードできません。上記のコンパイルスクリプトのように、1つのファイルにバンドルしてください。

プラグインの呼び出しが「not implemented on web」で拒否される。 そのプラグインにはWeb実装がありません。メインプロセスにIPCハンドラーを追加し、それを経由して呼び出してください。 window.desktop.

macOSで「アプリが破損しているか、未確認の開発元です」と表示される。 アプリが署名・公証されていません。electron-builderでDeveloper ID証明書と公証(notarization)を設定してください。

インストール直後にアップデートがロールバックされる。 notifyAppReady() タイムアウト(デフォルトは10秒)内に呼び出されませんでした。アプリ起動時に早めに呼び出してください。

まとめ

デスクトップ展開に専用の Capacitor プラットフォームパッケージは不要です。短いElectronメインプロセス、型付きの API を公開するプリロード、相対アセットパス、そしてelectron-builderがあれば、同じWebビルドから3つのOSすべてのインストーラーを作成できます。さらに、 @capgo/electron-updater デスクトップアプリでも、iOSやAndroidのビルドと同じ live update ワークフローが使えます。デスクトップランタイムを検討中の方は、こちらもご覧ください。 Electron vs Tauri の比較.

Capacitor アプリ向けの Live Updates

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

最新のブログ記事

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