この記事では、Electronにおける重要な機能の一つであるPreloadスクリプトについて、その概要と基本的な使い方をまとめています。
- Electronにおける、Prelaodスクリプトの概要と用途
- 基本的なPreloadスクリプトの実装方法と動作チェック

Preload の概要
Preload スクリプト は、一言で言うなら、Rendererプロセスで(安全に)Node.js機能を使用するための機能です。
通常、Node.jsの実行はメインプロセスのみに制限されており、レンダラープロセスでは実行することができません。
これに対し、Preload スクリプトはレンダラープロセスが読み込まれる前に(制限の適用前段階で)実行されるため、
Node.js API にアクセスできるタイミングで、安全なインターフェース(bridge)を構築します。
【補足】なぜ Preload スクリプトで Node.js API にアクセスできるのか?
Electron の Preload スクリプトが Node.js API にアクセスできるタイミングで安全なインターフェースを構築できる理由は、Electron の プロセスとセキュリティモデルの設計にあります。
Preload スクリプトは BrowserWindow
でページが読み込まれるより前、つまり最初の HTML/JS が動く前に実行されます。
new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
}
})
このタイミングでは、Electron のレンダラープロセスはまだ「Node.js API アクセスを拒否する設定(nodeIntegration: false
)」で動く準備が整っていません。
この処理フローのため、Preload だけは Node.js API にアクセスできるように特別に許可されているのです。
Preload スクリプトは開発者が書いたコードで、外部から読み込まれることは基本的にありません。
そのため、Electron 側は「Preload だけは Node.js を使っても良い」と安全とみなしています。
- Preload は「分離された独自のJavaScriptコンテキスト」で実行されます。
- レンダラーとは別世界なので、Node.js API を使っても、レンダラーからは直接見えません。
この仕組みによって、安全に contextBridge.exposeInMainWorld()
を使って、あらかじめフィルターされた API だけをレンダラーに渡せるのです。
基本的な構成と動作フロー
実際の構成と動作フローをまめると以下のようになります。
main.js(メインプロセス)
└── BrowserWindow の作成時に preload.js を指定
↓
preload.js(Preload スクリプト)
└── contextBridge と ipcRenderer を使って、安全な API を定義
↓
index.html(レンダラープロセス)
└── window.api などで preload.js 経由の API を利用
主な用途
Preloadの主な用途は以下の通りです。
用途 | 説明 |
---|---|
🔒 セキュアな通信 | contextBridge を用いて、Node.js の全機能を渡さずに必要な部分だけレンダラーに公開できる。 |
📡 IPC通信の仲介 | ipcRenderer を通じて、メインプロセスとのやり取りを可能にする(例:ファイル読み書き、OS操作)。 |
🛡️ Node.js 機能の制限 | レンダラーから直接 fs , os , child_process などにアクセスさせないことで、セキュリティリスクを減らす。 |
📁 環境変数や設定値の注入 | 起動時に Electron のバージョンやアプリの設定などを注入できる。 |
🔧 カスタムAPIの構築 | フロント側に使わせたい関数だけを window.api のような名前で渡すことが可能。 |
簡単な Preload スクリプトの実装
以下では、実際の例とともにPreloadスクリプトを実装するときの流れをまとめていきます。
※ここでの内容は、以下記事の内容をベースとしてすすめています。環境構築や設定については以下をご参照ください。

preloadスクリプト本体の作成
まずはElectronプロジェクトフォルダ直下に、preload.js
を作成し、以下のようなコードを記述します。
// Electron から必要なモジュールを読み込み
const { contextBridge, ipcRenderer } = require('electron');
// contextBridge を使って、window.api という名前でレンダラ側にAPIを公開
contextBridge.exposeInMainWorld('api', {
// メインプロセスへメッセージを送信する関数
sendMessage: (msg) => ipcRenderer.send('message', msg),
// メインプロセスからの返信メッセージを受け取るためのリスナーを登録
onReply: (callback) => ipcRenderer.on('reply', (_, data) => callback(data)),
});
メインプロセスでPreloadを読み込み
メインプロセス(main.js
)にて、作成したPreloadスクリプトを使用するための設定を追加します。
// Electron の主要モジュールを読み込み
const {
app, // アプリケーションのライフサイクル(起動・終了など)を制御
BrowserWindow, // ブラウザウィンドウ(レンダラー)の作成
ipcMain // メインプロセス側で、レンダラーとの通信を受け取る
} = require('electron');
const path = require('path'); // 絶対パス指定のため追加
console.log('main.js - start:OK') // 起動確認テスト用出力
// ウィンドウ作成関数
const createWindow = () => {
// 新規ウィンドウインスタンスを生成
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
// Preload スクリプトを指定
preload: path.join(__dirname, 'preload.js'),
// IPCセキュリティ設定
contextIsolation: true, // Preload とレンダラーの分離
nodeIntegration: false, // レンダラーで Node.js を制限
},
});
// 作成したウィンドウに `index.html` を読み込む(ローカルファイル)
win.loadFile('index.html');
};
// アプリの起動準備完了時
app.whenReady().then(() => {
// ウィンドウを生成
createWindow();
//macOS用:Dockから再クリック時などに呼ばれるイベント`activate`発生時
app.on('activate', () => {
//ウィンドウがすべて閉じられていた場合、新しいウィンドウを再作成
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
// すべてのウィンドウが閉じられた時
app.on('window-all-closed', () => {
// macOSではない場合、アプリ終了
if (process.platform !== 'darwin') app.quit();
});
// レンダラープロセスからで送られてくる "message" イベントを受信(Preload経由)
ipcMain.on('message', (event, arg) => {
// レンダラーから送られた内容をコンソールに表示
console.log('Renderer says:', arg);
// メインプロセスからレンダラープロセスへ返信メッセージを送信
// 'reply' チャンネルでメッセージを送る → preload.js で onReply() によって受信可能
event.reply('reply', 'Hello from main process');
});
補足:webPreferences 設定について
nodeIntegration: false
にして、Preload 経由でのみ Node.js を使わせる。contextIsolation: true
を有効にすることで、レンダラーが Preload 以外で Node.js API を触れないようにする- 上記二つはElectronのデフォルト値として設定されているため、コード記述は省略できます(例ではわかりやすさのために記述しています)
レンダラーでのAPI使用
レンダラー用のJavascriptrenderer.js
を作成し、以下のように構成します。
※ HTMLのCSP設定でインラインJavascriptは制限しているため、外部ファイルに分離して構成してください
// DOMが読み込まれたらイベント設定
window.addEventListener('DOMContentLoaded', () => {
const sendBtn = document.getElementById('sendBtn');
const responseP = document.getElementById('response');
// ボタンがクリックされたら、メッセージをメインプロセスに送信
sendBtn.addEventListener('click', () => {
window.api.sendMessage('こんにちは!Rendererからのメッセージです');
});
// メインプロセスからの返信を受け取って表示
window.api.onReply((data) => {
responseP.textContent = 'メインプロセスからの返信: ' + data;
});
});
作成したrenderer.js
は、HTMLで以下のように使用します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<!-- コンテンツセキュリティポリシー(CSP)設定:XSSなどを防ぐための読み込み制限-->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'" />
<meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'" />
<title>Electronテストページ</title>
</head>
<body>
<h1>Hello, Electron!</h1>
<p>これはRendererプロセスで表示されています。</p>
<!-- メッセージ送信ボタン -->
<button id="sendBtn">メッセージ送信</button>
<p id="response">メインプロセスからの返信はここに表示されます</p>
<!-- レンダラーJSファイルを読み込み -->
<script src="renderer.js"></script>
</body>
</html>
各通信の関係性
ここまでのコードでも扱った、各プロセス間通信のコードとその関係性は以下の通りになります。
処理 | 概要 |
---|---|
ipcRenderer.send('message', msg) | レンダラー → メインに送信 |
ipcMain.on('message', callback) | メインで受信し、処理 |
event.reply('reply', msg) | メイン → レンダラーへ返信 |
ipcRenderer.on('reply', callback) | レンダラー側で返信を受信 |
動作確認
package.json の登録したコマンド start
(“start”: “electron .”) でElectronアプリを起動できます。
npm run start
起動したアプリのボタンをクリックすることで、アプリの表示ページとコンソールそれぞれに送信メッセージが表示されます。

【補足】コンソールで日本語表示が文字化けする場合
日本語が コンソールに表示されたときに文字化けする問題は、通常 コンソールの出力エンコーディングによるものです。
Windows の標準コンソール(cmd.exe や PowerShell)は、デフォルトで Shift_JIS(CP932) を使用しますが、Electron(Node.js)は UTF-8 を使ってログ出力します。
そのため、UTF-8 の日本語が Shift_JIS 環境で正しく表示できず、文字化けが起きます。
対処として、以下コマンドでUTF-8(65001)を使うようにコンソールの設定を行うことで正しい表示を確認できます。
※Visual Studio Code のターミナルでも同様に有効です
chcp 65001
元に戻すときは、以下コマンドを実行します。
chcp 932
また、現在の設定状態を確認することもできます。
chcp
現在のコード ページ: 65001