electron-best-practices について
React で Electron アプリを開発する開発者やエージェント向けのスキルです。安全な設計、型安全な IPC、プロジェクト設定、パッケージ化、署名、テストを扱い、セキュリティ分析やスキャフォールド、IPC 型生成のスクリプトも含みます。
主な機能
- 01contextBridge とサンドボックスの安全パターン
- 02型安全な IPC 通信パターン
- 03electron-vite と Electron Forge の設定
- 04パッケージ化、コード署名、公証のガイダンス
- 05セキュリティ分析、スキャフォールド、型生成スクリプト
- 06Playwright と単体テストのガイダンス
活用シーン
- 01Electron + React アプリのひな型作成
- 02メインプロセスとレンダラープロセス間の通信実装
- 03Electron プロジェクトのセキュリティ設定の監査
- 04デスクトップアプリのパッケージ化と署名の準備
- 05Electron アプリのエンドツーエンドテスト作成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ ninjasln-labs/ agent-skills --skill electron-best-practicesインストール後、Claude Code で/electron-best-practicesを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
assets/configs/electron-vite.config.ts.mdforge.config.js.mdplaywright.config.ts.mdtsconfig.json.mdassets/examples/multi-window-example.mdtyped-ipc-example.mdassets/templates/ipc-handler.ts.mdmain-process.ts.mdpreload-script.ts.mdreact-root.tsx.mdreferences/architecture/multi-window-state.mdprocess-separation.mdproject-structure.mdreferences/integration/react-patterns.mdstate-management.mdreferences/ipc/electron-trpc.mderror-serialization.mdtyped-ipc.mdreferences/packaging/auto-updates.mdbundle-optimization.mdci-cd-patterns.mdcode-signing.mdreferences/security/context-isolation.mdcsp-and-permissions.mdsecurity-checklist.mdreferences/testing/playwright-e2e.mdtest-structure.mdunit-testing.mdreferences/tooling/electron-forge.mdelectron-vite.md詳細
Electron + React のベストプラクティス
中文クイックガイド(クイックガイド)
- できること:React を使った Electron アプリ開発におけるセキュリティ、型安全な IPC、プロセス連携、パッケージ化・署名・公証、テストのパターンを示し、分析、スキャフォールディング生成、型生成のための参考資料を提供します。
- 利用する場面:メイン/preload/レンダラープロセスのコード作成、electron-vite または Forge の設定、IPC の構築、contextBridge/sandbox/CSP によるセキュリティ対策、署名・公証、Playwright を使った Electron アプリのテストを行うときに利用します。Tauri、純粋な Web アプリ、Electron 20 未満のバージョン、React 以外のレンダラーフレームワークには使用しません。
- 基本手順:①まず安全なデフォルト設定を適用します(contextIsolation、sandbox、nodeIntegration の無効化、メインプロセスとレンダラープロセス間の通信はすべて contextBridge 経由にする)。②
references/からパターンを選びます(型付き IPC、プロセス分離、React 連携)。③electron-viteまたは Forge でパッケージ化し、コード署名/公証を行います。④Playwright によるエンドツーエンドテストと Jest/Vitest による単体テストで検証します。 - 中国国内からのアクセス性:パターン、参考資料、テンプレートはすべてこのパッケージ内(
references/、assets/templates/)に含まれており、ネット接続なしで利用できます。本文に記載されている任意の作業には外部環境が必要です。具体的には、GitHub Actions のマトリックスビルドと、deno runによるscripts/配下の便利なスクリプトの実行です。利用できない場合は、本文に記載された確認可能な出力をもとに対応し、実行できなかった手順を明示してください。
AI エージェントが React を使って安全で本番運用に対応できる Electron アプリを構築できるよう支援します。このスキルでは、セキュリティパターン、型安全な IPC 通信、プロジェクト設定のガイダンス、パッケージ化とコード署名のワークフローに加え、分析、スキャフォールディング、型生成のためのツールを提供します。
このスキルを使用する場面
次のような場合にこのスキルを使用してください:
- Electron のメイン、preload、またはレンダラープロセスのコードを生成するとき
- electron-vite または Electron Forge を設定するとき
- プロセス間の IPC 通信を設定するとき
- セキュリティパターン(contextBridge、sandbox、CSP)を実装するとき
- デスクトップアプリをパッケージ化、署名、公証するとき
- Playwright で Electron アプリをテストするとき
- マルチウィンドウのアーキテクチャを設計するとき
次の場合、このスキルは使用しないでください:
- Tauri アプリを構築するとき(仕組みが異なるため、Tauri 固有のガイダンスを使用してください)
- デスクトップ向けの要件がない純粋な Web アプリを構築するとき
- Electron 20 未満のバージョンを対象とするとき(セキュリティのデフォルト設定が異なります)
- React 以外のレンダラーフレームワークを使用するとき(フレームワーク固有のスキルを使用してください)
基本原則
1. セキュリティを最優先するアーキテクチャ
最新の Electron では、Electron 20 以降で標準となった 3 つのデフォルト設定がセキュリティの要となります。context isolation、sandbox モード、nodeIntegration の無効化です。いずれかを無効にすると、XSS 攻撃によって完全なリモートコード実行に発展する可能性があります。メインプロセスとレンダラープロセス間の通信は、すべて contextBridge 経由にする必要があります。
// preload.ts - SECURE pattern
contextBridge.exposeInMainWorld('electronAPI', {
loadPreferences: () => ipcRenderer.invoke('load-prefs'),
saveFile: (content: string) => ipcRenderer.invoke('save-file', content),
onUpdateCounter: (callback: (value: number) => void) => {
const handler = (_event: IpcRendererEvent, value: number) => callback(value);
ipcRenderer.on('update-counter', handler);
return () => ipcRenderer.removeListener('update-counter', handler);
}
});
ローカルファイルを読み込むアプリでは、HTTP ヘッダーで Content Security Policy を設定し、スクリプトのソースを 'self' に制限してください。
2. 型安全な IPC 通信
リクエストとレスポンスの通信には、send/on より invoke/handle パターンを推奨します。適切な async/await のセマンティクスとエラー伝播が得られます。型付きチャンネルには、次のマップ型パターンを使用してください。
type IpcChannelMap = {
'load-prefs': { args: []; return: UserPreferences };
'save-file': { args: [content: string]; return: { success: boolean } };
};
複雑なアプリケーションでは、electron-trpc を使うと、tRPC のルーターパターンと Zod による検証を利用した完全な型安全性を実現できます。
export const appRouter = t.router({
greeting: t.procedure
.input(z.object({ name: z.string() }))
.query(({ input }) => `Hello, ${input.name}!`),
});
IPC 境界を越えるエラーの処理には注意が必要です。Electron が Error オブジェクトの message プロパティしかシリアライズしないためです。完全なエラーコンテキストを保持するには、レスポンスを { success, data, error } の結果型でラップしてください。
3. 最新のプロジェクト設定
推奨スタックでは、開発に electron-vite、パッケージ化に Electron Forge を使用します。electron-vite は、メイン、preload、レンダラープロセスを一元管理する統合設定を提供し、1 秒未満での開発サーバー起動と即時の HMR を実現します。Electron Forge は、署名と公証に Electron のファーストパーティパッケージを使用します。
src/
├── main/ # Main process (Node.js environment)
│ ├── index.ts
│ └── ipc/ # IPC handlers
├── preload/ # Secure bridge via contextBridge
│ ├── index.ts
│ └── index.d.ts # TypeScript declarations for exposed APIs
└── renderer/ # React application (pure web, no Node access)
├── src/
└── index.html
4. React 連携パターン
React 18 の並行処理機能は、Electron の Chromium ベースのレンダラーでも通常どおり動作します。Strict Mode ではエフェクトが二重に呼び出されるため、メモリーの問題につながる IPC リスナーのリークを検出できます。IPC リスナーを登録するエフェクトからは、必ずクリーンアップ関数を返してください。
useEffect(() => {
const cleanup = window.electronAPI.onUpdateCounter((value) => {
setCount(value);
});
return cleanup;
}, []);
マルチウィンドウアプリケーションでは、共有状態の単一の信頼できる情報源としてメインプロセスを使用してください。electron-store による永続化と IPC ブロードキャストを組み合わせれば、どのウィンドウで変更しても、ほかのすべてのウィンドウに反映されます。
クイックリファレンス
| カテゴリ | 推奨 | 避ける |
|---|---|---|
| セキュリティ | contextBridge.exposeInMainWorld() | nodeIntegration: true |
| IPC | invoke/handle パターン | リクエストとレスポンスに send/on を使用する |
| プリロード | 型付き関数ラッパー | 生の ipcRenderer を公開する |
| ビルドツール | electron-vite | webpack ベースのツールチェーン |
| パッケージング | Electron Forge | 手動でのパッケージング |
| 状態管理 | Zustand + electron-store | シンプルなアプリで Redux を使用する |
| テスト | Playwright E2E | Spectron(非推奨) |
| アップデート | electron-updater | アップデートを手動で確認する |
| 署名 | CI 統合型のコード署名 | 署名なしのリリース |
| CSP | HTTP ヘッダーで 'self' のみ | CSP を設定しない |
| エラー処理 | 結果型 {success, data, error} | IPC 経由で生の Error を渡す |
| マルチウィンドウ | メインプロセスを状態ハブにする | ウィンドウ間で直接やり取りする |
コード生成ガイドライン
Electron コードを生成する際は、次のパターンに従ってください。
BrowserWindow の作成
const win = new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, '../preload/index.js'),
contextIsolation: true,
sandbox: true,
nodeIntegration: false,
},
});
contextIsolation と sandbox は常に有効にしてください。nodeIntegration は絶対に有効にしないでください。プリロードのパスは、ビルド済みの出力先を指すようにしてください。
IPC ハンドラーモジュール
export function registerFileHandlers(): void {
ipcMain.handle('save-file', async (_event, content: string) => {
try {
await fs.writeFile(filePath, content);
return { success: true, data: filePath };
} catch (err) {
return { success: false, error: (err as Error).message };
}
});
}
関連するハンドラーはモジュールにまとめてください。すべての戻り値に結果型パターンを使用してください。レンダラープロセスから受け取るすべての引数を検証してください。
よくあるアンチパターン
Electron コードを生成する際は、次のパターンを避けてください。
| アンチパターン | 問題 | 解決策 |
|---|---|---|
nodeIntegration: true | XSS が完全な RCE に発展する | 無効のままにする(デフォルト) |
ipcRenderer を直接公開する | レンダラーから IPC に完全にアクセスできる | contextBridge 関数でラップする |
contextIsolation がない | レンダラーからプリロードのスコープにアクセスできる | 有効のままにする(Electron 12 以降のデフォルト) |
| コード署名がない | OS のセキュリティ警告が表示され、Gatekeeper にブロックされる | すべてのプラットフォーム向けに署名し、公証する |
sandbox のない BrowserWindow | プリロードから Node.js に完全にアクセスできる | sandbox を有効にする(Electron 20 以降のデフォルト) |
| 検証されていない IPC 引数 | レンダラーからのインジェクション攻撃 | Zod または手動チェックで検証する |
0.0.0.0 へのサーバーバインド | ローカルサーバーがネットワークに公開される | 常に 127.0.0.1 にバインドする |
| CSP ヘッダーがない | スクリプトインジェクションの攻撃経路になる | HTTP ヘッダーで厳格な CSP を設定する |
| IPC エラーのシリアライズがない | 境界を越える際にエラーのコンテキストが失われる | 結果型パターンを使用する |
| テストに Spectron を使用する | 非推奨で、対応する Electron は最大 13 | Playwright を使用する |
セキュリティ監査チェックリストの全文は references/security/security-checklist.md を参照してください。
スクリプトリファレンス
analyze-security.ts
Electron プロジェクトのセキュリティ設定ミスを分析します。
deno run --allow-read scripts/analyze-security.ts <path> [options]
Options:
--strict Enable all checks
--json Output JSON for CI
-h, --help Show help
Examples:
# Analyze a project
deno run --allow-read scripts/analyze-security.ts ./src
# Strict mode for CI pipeline
deno run --allow-read scripts/analyze-security.ts ./src --strict --json
scaffold-electron-app.ts
安全なデフォルト設定を備えた、新しい Electron + React プロジェクトをスキャフォールドします。
deno run --allow-read --allow-write scripts/scaffold-electron-app.ts [options]
Options:
--name <name> App name (required)
--path <path> Target directory (default: ./)
--with-react Include React setup
--with-trpc Include electron-trpc
--with-tests Include Playwright tests
Examples:
# Basic app with React
deno run --allow-read --allow-write scripts/scaffold-electron-app.ts \
--name "my-app" --with-react
# Full setup with trpc and tests
deno run --allow-read --allow-write scripts/scaffold-electron-app.ts \
--name "my-app" --with-react --with-trpc --with-tests
generate-ipc-types.ts
IPC ハンドラーファイルから TypeScript 型定義を生成します。
deno run --allow-read --allow-write scripts/generate-ipc-types.ts [options]
Options:
--handlers <path> Path to IPC handler files
--output <path> Output path for type definitions
--validate Validate existing types match handlers
Examples:
# Generate types from handlers
deno run --allow-read --allow-write scripts/generate-ipc-types.ts \
--handlers ./src/main/ipc --output ./src/preload/ipc-types.d.ts
# Validate types in CI
deno run --allow-read scripts/generate-ipc-types.ts \
--handlers ./src/main/ipc --validate
追加リソース
セキュリティ
references/security/context-isolation.md- contextBridge と分離のパターンreferences/security/csp-and-permissions.md- コンテンツセキュリティポリシーの設定references/security/security-checklist.md- セキュリティ監査チェックリストの全文
IPC 通信
references/ipc/typed-ipc.md- 型付きチャネルマップのパターンreferences/ipc/electron-trpc.md- 完全な型安全性を実現する tRPC 統合references/ipc/error-serialization.md- IPC 境界を越える結果型
アーキテクチャ
references/architecture/project-structure.md- ディレクトリ構成references/architecture/process-separation.md- メイン、プリロード、レンダラーの役割references/architecture/multi-window-state.md- ウィンドウ間での状態共有
React 統合
references/integration/react-patterns.md- useEffect のクリーンアップ、Strict Modereferences/integration/state-management.md- Zustand と electron-store のパターン
パッケージ化と配布
references/packaging/code-signing.md- プラットフォーム固有の署名ワークフローreferences/packaging/auto-updates.md- electron-updater の設定references/packaging/bundle-optimization.md- サイズ削減の手法references/packaging/ci-cd-patterns.md- GitHub Actions のマトリックスビルド
テスト
references/testing/playwright-e2e.md- Playwright の Electron サポートreferences/testing/unit-testing.md- Jest/Vitest のマルチプロジェクト設定references/testing/test-structure.md- テスト構成のパターン
ツール
references/tooling/electron-vite.md- ビルドツールの設定references/tooling/electron-forge.md- パッケージ化と配布references/tooling/tauri-comparison.md- Tauri を選ぶ場合
テンプレート
assets/templates/main-process.ts.md- メインプロセスのスターターテンプレートassets/templates/preload-script.ts.md- contextBridge を使用するプリロードスクリプトassets/templates/ipc-handler.ts.md- IPC ハンドラーモジュールのテンプレートassets/templates/react-root.tsx.md- React ルートコンポーネントのテンプレート
設定例
assets/configs/electron-vite.config.ts.md- electron-vite の設定assets/configs/forge.config.js.md- Electron Forge の設定assets/configs/tsconfig.json.md- TypeScript 設定プリセットassets/configs/playwright.config.ts.md- Playwright の Electron テスト設定
完全な例
assets/examples/typed-ipc-example.md- 型付き IPC のエンドツーエンド解説assets/examples/multi-window-example.md- 複数ウィンドウの状態管理
最小限の実例
前提条件: 上記のセキュアなデフォルト設定(contextIsolation: true、sandbox: true、contextBridge を介した preload)を使用する Electron + React アプリです。
呼び出し:「レンダラーからファイルを保存できるようにし、エラーを握りつぶさずにユーザーに表示してください。」
結果の抜粋(メインプロセスで登録 → preload で公開 → レンダラーで呼び出し、エラー状態を表示):
// src/main/ipc/save-file.ts — main process registers the handler
ipcMain.handle('save-file', async (_event, content: string) => {
try {
await fs.writeFile(filePath, content);
return { success: true, data: filePath };
} catch (err) {
return { success: false, error: (err as Error).message };
}
});
// src/preload/index.ts — preload exposes exactly this one function
contextBridge.exposeInMainWorld('electronAPI', {
saveFile: (content: string) => ipcRenderer.invoke('save-file', content),
});
// renderer — invoke, then map the Result type to a user-visible state
async function handleSave(content: string) {
const result = await window.electronAPI.saveFile(content);
if (!result.success) {
setStatus({ kind: 'error', message: `Couldn't save file: ${result.error}` });
return;
}
setStatus({ kind: 'saved', path: result.data });
}
ハンドラーは拒否を生の Error として外部に伝播させません。Result 型が境界を越え、レンダラーは以下の「ユーザーへのエラー表示」に従って error を画面に表示される状態へ変換します。
ユーザーへのエラー表示
Result 型のパターンは IPC 境界向けのものです。レンダラーでは、人が対処できる情報を表示する必要があります。コードを生成するときは、次の点に従ってください。
- すべての IPC 失敗をユーザーに見える状態に変換してください。トリガーとなったコントロール付近にインラインメッセージを表示する(フォーム)、トーストまたはバナーを表示する(バックグラウンド操作)、エラー境界を設ける(レンダリングのクラッシュ)などです。拒否されたプロミスを握りつぶしてはいけません。
- 「エラーが発生しました」だけでなく、原因と次の手順を示してください。例:
Couldn't save file: disk is full. Free up space or choose another location.Result 型のmessage(この目的で境界を越えているもの)は含めますが、生のスタックトレースは決して表示しません。 - 再試行可能なエラーと致命的なエラーを区別してください。ネットワークやオフラインの失敗には「再試行」アクションを表示し、権限や検証の失敗には再試行ボタンではなく手順を示してください。
- 完全なコンテキスト(チャネル名、引数の形状)をメインプロセスのログに記録し、UI には要約したメッセージだけを表示してください。
スクリプトの失敗と終了
同梱されている3つのスクリプトはすべてDenoで実行されます。各失敗には、確認できる症状と終了結果があります。
| 症状 | 原因 | 対処 |
|---|---|---|
command not found: deno | Denoがインストールされていません | Denoをインストールする(deno.comを参照)か、手動で進めます。references/とassets/templates/のパターンを手動で適用してください。スクリプトは便利なものであり、必須ではありません。要求されたスキャフォールドや分析を黙って省略しないでください。実行しなかった手順をユーザーに伝えてください。 |
パスの読み取り時にPermissionDenied | --allow-readがありません(スキャフォールド/型生成では--allow-writeも必要です) | 上記のスクリプトリファレンスに記載されているフラグを付けて再実行してください。これらは文書化されている最小要件です。 |
analyze-security.tsが--strict --jsonでゼロ以外の終了コードを返す | チェックに失敗しました(これはレポートであり、クラッシュではありません) | JSON出力は修正すべき検出事項として扱い、環境エラーとは見なさないでください。クラッシュ時には、JSONレポートではなくスタックトレースが出力されます。 |
generate-ipc-types.ts --validateがゼロ以外の終了コードを返す | 既存の型定義がハンドラーと一致しなくなっています | 型を再生成してから、--validateを再実行し、終了コードが0であることを確認してください。 |
scaffold-electron-app.tsが書き込みを拒否する | 対象ディレクトリが空ではないか、--nameがありません | 必須の--nameを指定するか、--pathに空のディレクトリを指定してください。このスクリプトは既存のファイルを上書きしません。 |
| 実行時:アプリからのネットワークリクエストが失敗する(タイムアウト/オフライン状態。メインプロセスのログで確認できます) | 一時的な障害、またはホストに到達できません | 回数を限定して(例:バックオフを伴って2~3回)再試行し、その後は「ユーザーへのエラーの表示」に従って、再試行アクションのあるユーザーに見えるエラー状態に移行してください。無制限に黙って再試行するループは決して行わないでください。 |
やってはいけないこと → 解決方法(FAQ)
| やってはいけないこと | 確認できる症状 | 解決方法 |
|---|---|---|
「1つのウィンドウだけ」でnodeIntegrationを有効にする | レンダラーからrequire()を実行でき、セキュリティチェックリストで警告されます | 無効のままにし、必要な関数だけをcontextBridge経由で公開してください。 |
リクエストと応答の呼び出しにsend/onを使う | 応答が競合しやすく、結果をawaitできません | invoke/handleを使用してください。 |
IPC経由でErrorオブジェクトを渡す | レンダラーにはメッセージのない{}のようなオブジェクトが届きます | Result型{ success, data, error }を返してください。 |
CSPを設定せずにBrowserWindowでリモートURLを読み込む | Content Security Policyのチェックリスト項目に違反します | HTTPヘッダーで厳格なCSPを設定し、ローカルコンテンツでは'self'のみを許可してください。 |
| Spectronでテストする | 非推奨で、Electron 13までしか対応していません | Playwrightを使用してください(references/testing/playwright-e2e.md)。 |
| Tauriまたは純粋なWebアプリにこのスキルを使う | ガイダンスがランタイムに適用されません | 使用を中止してください。上記の「適用しない場合」の境界で、これらのランタイムについて説明しています。 |