Warning
This project is in alpha stage. API may change significantly.
Electron main process bridge - expose main process functions to renderer process via JSON-RPC.
electron-main-bridge is a library that securely exposes Electron's main process APIs to the renderer process while maintaining full type safety. In Electron, access to Node.js APIs and main-process modules is restricted in the renderer for security reasons. This library provides a bridge using electron-json-rpc that lets you call main process functions from your renderer code as if they were local functions.
- No more preload boilerplate - Stop writing repetitive IPC handlers and preload scripts
- Type-safe IPC calls - Get full TypeScript autocomplete and type checking for cross-process calls
- Security-focused - Built with
contextIsolationenabled by default - Modular architecture - Only bundle the APIs you actually use
┌─────────────────┐ IPC/JSON-RPC ┌─────────────────┐
│ Renderer │ ─────────────────────> │ Main Process │
│ │ │ │
│ fs.readFile() │ │ Native fs API │
│ app.getVersion()│ │ Native app API │
└─────────────────┘ └─────────────────┘
- Main Process: Register RPC handlers for Electron APIs you want to expose
- Preload: Expose a typed RPC client via
contextBridge - Renderer: Call main process functions with full TypeScript support
| Module | Description |
|---|---|
fs |
File system operations (read, write, stat, mkdir, etc.) |
path |
Path manipulation (join, resolve, dirname, etc.) |
app |
Application lifecycle and metadata |
autoUpdater |
Automatic application updates (macOS/Windows) |
dialog |
Native file/folder dialogs |
globalShortcut |
Global keyboard shortcuts |
session |
Session management (cookies, cache, downloads) |
menu |
Application and context menus |
notification |
Desktop notifications |
screen |
Display and screen information |
shell |
File association and external URL opening |
tray |
System tray icons |
browserWindow |
Window management |
powerMonitor |
System power state monitoring |
powerSaveBlocker |
Prevent system sleep |
safeStorage |
Secure credential storage |
systemPreferences |
System-level preferences |
pushNotifications |
APNS push notifications (macOS) |
shareMenu |
Native share sheet (macOS) |
- 📦 Modular architecture - import only the modules you need, keep bundle size minimal
- 🔒 Type-safe API - full TypeScript support with autocomplete and type checking
- 🚀 IPC-compatible types - handles serialization of complex types like
Stats,Dirent,Date - ⚡ Zero boilerplate - no more repetitive IPC handlers and preload scripts
- 🔌 Easy integration - plug and play with Electron apps
| Format | Minified | Gzipped |
|---|---|---|
| Main (fs) | 0.90 kB | 0.31 kB |
| Main (browserWindow) | 8.88 kB | 1.46 kB |
| Renderer (fs) | 3.30 kB | 0.81 kB |
| Renderer (browserWindow) | 8.88 kB | 1.46 kB |
bun add electron-main-bridgeimport { app } from "electron";
import { RpcServer } from "electron-json-rpc/main";
import { registerFsModule } from "electron-main-bridge/main/fs";
import { registerPathModule } from "electron-main-bridge/main/path";
import { registerAppModule } from "electron-main-bridge/main/app";
import { registerAutoUpdaterModule } from "electron-main-bridge/main/auto-updater";
import { registerDialogModule } from "electron-main-bridge/main/dialog";
import { registerGlobalShortcutModule } from "electron-main-bridge/main/globalShortcut";
const rpc = new RpcServer();
registerFsModule(rpc);
registerPathModule(rpc);
registerAppModule(rpc);
registerAutoUpdaterModule(rpc);
registerDialogModule(rpc);
registerGlobalShortcutModule(rpc);
rpc.listen();import { contextBridge, ipcRenderer } from "electron";
import { exposeRpcApi } from "electron-json-rpc/preload";
exposeRpcApi({ contextBridge, ipcRenderer });import * as fs from "electron-main-bridge/renderer/fs";
import * as path from "electron-main-bridge/renderer/path";
import * as app from "electron-main-bridge/renderer/app";
import * as autoUpdater from "electron-main-bridge/renderer/auto-updater";
import { dialog } from "electron-main-bridge/renderer/dialog";
import * as globalShortcut from "electron-main-bridge/renderer/globalShortcut";
// File operations
await fs.writeFile("/path/to/file", "Hello, World!");
const content = await fs.readFile("/path/to/file", "utf-8");
// Path operations
const fullPath = await path.join("/home", "user", "docs");
const userData = await app.getPath("userData");
// App info
const version = await app.getVersion();
const isReady = await app.isReady();
// Dialog operations
const { filePaths } = await dialog.showOpenDialog({
properties: ["openFile"],
filters: [{ name: "Text Files", extensions: ["txt"] }],
});
// Events
app.on("window-all-closed", () => {
console.log("All windows closed");
});
const unsubscribe = app.on("browser-window-created", ({ id, title }) => {
console.log(`Window created: ${id} - ${title}`);
});
// Global shortcuts
await globalShortcut.register("CommandOrControl+X");
await globalShortcut.register("CommandOrControl+C", "CommandOrControl+V");
// Listen for shortcut events
globalShortcut.onAccelerator((accelerator) => {
console.log(`Shortcut pressed: ${accelerator}`);
});| Function | Return Type |
|---|---|
readFile(path, options?) |
Promise<string | Uint8Array> |
writeFile(path, data, options?) |
Promise<void> |
appendFile(path, data, options?) |
Promise<void> |
copyFile(src, dest, mode?) |
Promise<void> |
unlink(path) |
Promise<void> |
rename(oldPath, newPath) |
Promise<void> |
truncate(path, len?) |
Promise<void> |
stat(path, options?) |
Promise<FsStats> |
lstat(path, options?) |
Promise<FsStats> |
exists(path) |
Promise<boolean> |
access(path, mode?) |
Promise<void> |
chmod(path, mode) |
Promise<void> |
chown(path, uid, gid) |
Promise<void> |
utimes(path, atime, mtime) |
Promise<void> |
mkdir(path, options?) |
Promise<void | string> |
readdir(path, options?) |
Promise<string[] | FsDirent[]> |
rmdir(path, options?) |
Promise<void> |
rm(path, options?) |
Promise<void> |
cp(source, dest, options?) |
Promise<void> |
link(existingPath, newPath) |
Promise<void> |
symlink(target, path, type?) |
Promise<void> |
readlink(path) |
Promise<string> |
realpath(path) |
Promise<string> |
| Function | Return Type |
|---|---|
parse(p) |
Promise<ParsedPath> |
format(pathObject) |
Promise<string> |
join(...segments) |
Promise<string> |
resolve(...segments) |
Promise<string> |
normalize(p) |
Promise<string> |
dirname(p) |
Promise<string> |
basename(p, ext?) |
Promise<string> |
extname(p) |
Promise<string> |
relative(from, to) |
Promise<string> |
isAbsolute(p) |
Promise<boolean> |
sep() |
Promise<string> |
delimiter() |
Promise<string> |
cwd() |
Promise<string> |
win32.* |
Promise<*> |
posix.* |
Promise<*> |
| Function | Return Type |
|---|---|
getName() |
Promise<string> |
setName(name) |
Promise<void> |
getVersion() |
Promise<string> |
getAppPath() |
Promise<string> |
getLocale() |
Promise<string> |
getSystemLocale() |
Promise<string> |
getPreferredSystemLanguages() |
Promise<string[]> |
getLocaleCountryCode() |
Promise<string> |
isReady() |
Promise<boolean> |
| Function | Return Type |
|---|---|
getPath(name) |
Promise<string> |
setPath(name, path) |
Promise<void> |
setAppLogsPath([path]) |
Promise<void> |
| Function | Return Type |
|---|---|
quit() |
Promise<void> |
exit([exitCode]) |
Promise<void> |
relaunch([options]) |
Promise<void> |
focus([options]) |
Promise<void> |
hide() |
Promise<void> |
show() |
Promise<void> |
| Function | Return Type |
|---|---|
addRecentDocument(path) |
Promise<void> |
clearRecentDocuments() |
Promise<void> |
setAsDefaultProtocolClient(protocol) |
Promise<boolean> |
requestSingleInstanceLock([data]) |
Promise<boolean> |
setBadgeCount([count]) |
Promise<void> |
getBadgeCount() |
Promise<number> |
getLoginItemSettings([options]) |
Promise<LoginItemSettings> |
setLoginItemSettings(settings) |
Promise<void> |
isAccessibilitySupportEnabled() |
Promise<boolean> |
setAccessibilitySupportEnabled(enabled) |
Promise<void> |
showAboutPanel() |
Promise<void> |
setAboutPanelOptions(options) |
Promise<void> |
getGPUFeatureStatus() |
Promise<GPUFeatureStatus> |
getGPUInfo(infoType) |
Promise<unknown> |
getAppMetrics() |
Promise<ProcessMetric[]> |
isEmojiPanelSupported() |
Promise<boolean> |
showEmojiPanel() |
Promise<void> |
getFileIcon(path, [options]) |
Promise<SerializedNativeImage> |
| Event | Data Type |
|---|---|
window-all-closed |
void |
quit |
{ exitCode: number } |
browser-window-created |
{ id: number; title: string } |
browser-window-focus |
{ id: number; title: string } |
browser-window-blur |
{ id: number; title: string } |
web-contents-created |
{ id: number } |
open-file |
{ path: string } |
open-url |
{ url: string } |
second-instance |
{ argv: string[]; workingDirectory: string } |
accessibility-support-changed |
{ enabled: boolean } |
// Subscribe to events
app.on("window-all-closed", () => {});
app.once("quit", ({ exitCode }) => {});
app.off("browser-window-created");Note
The autoUpdater module only supports macOS and Windows. Linux is not supported.
| Function | Return Type |
|---|---|
checkForUpdates() |
Promise<void> |
quitAndInstall() |
Promise<void> |
setFeedURL(options) |
Promise<void> |
getFeedURL() |
Promise<string> |
| Event | Data Type |
|---|---|
checking-for-update |
void |
update-available |
void |
update-not-available |
void |
update-downloaded |
UpdateInfo |
error |
UpdateError |
before-quit-for-update |
void |
import { autoUpdater } from "electron-main-bridge/renderer/auto-updater";
// Set the update feed URL (must be called before checkForUpdates)
await autoUpdater.setFeedURL({
url: "https://example.com/updates",
headers: { Authorization: "Bearer token" },
});
// Check for updates
await autoUpdater.checkForUpdates();
// Subscribe to events
autoUpdater.on("update-available", () => {
console.log("Update available!");
});
autoUpdater.on("update-downloaded", ({ releaseNotes, releaseName }) => {
console.log(`Update downloaded: ${releaseName}`);
// Notify user and ask if they want to restart
});
autoUpdater.on("error", ({ message }) => {
console.error("Update error:", message);
});
// Quit and install the update
await autoUpdater.quitAndInstall();| Function | Return Type |
|---|---|
showOpenDialog([options]) |
Promise<OpenDialogReturnValue> |
showSaveDialog([options]) |
Promise<SaveDialogReturnValue> |
showMessageBox(options) |
Promise<MessageBoxReturnValue> |
showErrorBox(title, content) |
Promise<void> |
import { dialog } from "electron-main-bridge/renderer/dialog";
// Open file dialog
const { filePaths, canceled } = await dialog.showOpenDialog({
properties: ["openFile", "multiSelections"],
filters: [
{ name: "Images", extensions: ["jpg", "png", "gif"] },
{ name: "All Files", extensions: ["*"] },
],
});
if (!canceled && filePaths.length > 0) {
console.log("Selected files:", filePaths);
}
// Save file dialog
const { filePath } = await dialog.showSaveDialog({
defaultPath: "untitled.txt",
filters: [{ name: "Text Files", extensions: ["txt"] }],
});
if (filePath) {
console.log("Save path:", filePath);
}
// Show message box
const { response, checkboxChecked } = await dialog.showMessageBox({
type: "question",
title: "Confirm",
message: "Are you sure?",
buttons: ["Yes", "No"],
checkboxLabel: "Remember my choice",
});
console.log("Selected button:", response);
console.log("Checkbox checked:", checkboxChecked);
// Show error box
await dialog.showErrorBox("Error", "Something went wrong!");Note
Global shortcuts can be registered even when the application does not have focus.
| Function | Return Type |
|---|---|
register(accelerator) |
Promise<boolean> |
registerAll(accelerators) |
Promise<boolean[]> |
isRegistered(accelerator) |
Promise<boolean> |
unregister(accelerator) |
Promise<void> |
unregisterAll() |
Promise<void> |
onAccelerator(callback) |
() => void |
import * as globalShortcut from "electron-main-bridge/renderer/globalShortcut";
// Register a single shortcut
const success = await globalShortcut.register("CommandOrControl+X");
if (!success) {
console.log("Shortcut registration failed");
}
// Register multiple shortcuts
const results = await globalShortcut.registerAll([
"CommandOrControl+C",
"CommandOrControl+V",
"CommandOrControl+A",
]);
console.log("Registration results:", results);
// Check if shortcut is registered
const isRegistered = await globalShortcut.isRegistered("CommandOrControl+X");
console.log("Shortcut registered:", isRegistered);
// Listen for shortcut events
const unsubscribe = globalShortcut.onAccelerator((accelerator) => {
console.log(`Shortcut pressed: ${accelerator}`);
// Handle different shortcuts
switch (accelerator) {
case "CommandOrControl+X":
// Cut operation
break;
case "CommandOrControl+C":
// Copy operation
break;
case "CommandOrControl+V":
// Paste operation
break;
}
});
// Unregister a specific shortcut
await globalShortcut.unregister("CommandOrControl+X");
// Unregister all shortcuts (e.g., when quitting)
await globalShortcut.unregisterAll();
// Stop listening for events
unsubscribe();Note
The session module requires passing a Session instance (e.g., win.webContents.session) when registering.
| Function | Return Type |
|---|---|
setDownloadPath(path) |
Promise<void> |
downloadURL(url) |
Promise<string> |
getCacheSize() |
Promise<number> |
clearCache() |
Promise<void> |
clearStorageData([options]) |
Promise<void> |
clearData([options]) |
Promise<void> |
getStoragePath() |
Promise<string | null> |
isPersistent() |
Promise<boolean> |
getUserAgent() |
Promise<string> |
setUserAgent(options) |
Promise<void> |
getSessionHash() |
Promise<string> |
| Function | Return Type |
|---|---|
item.pause(id) |
Promise<void> |
item.resume(id) |
Promise<void> |
item.cancel(id) |
Promise<void> |
item.setSavePath(id, path) |
Promise<void> |
| Event | Data Type |
|---|---|
will-download |
{ item: DownloadItem, sessionHash? } |
download-progress |
{ id, receivedBytes, totalBytes, sessionHash? } |
download-done |
{ id, state, path, sessionHash? } |
import { session } from "electron-main-bridge/renderer/session";
// Set download directory
await session.setDownloadPath("/path/to/downloads");
// Start a download
const url = await session.downloadURL("https://example.com/file.zip");
// Listen for download events
session.onWillDownload(({ item }) => {
console.log("Download started:", item.filename);
});
session.onDownloadProgress(({ id, receivedBytes, totalBytes }) => {
const percent = ((receivedBytes / totalBytes) * 100).toFixed(2);
console.log(`Progress: ${percent}%`);
});
session.onDownloadDone(({ id, state, path }) => {
console.log(`Download ${state}:`, path);
});
// If you use multiple sessions, use the session hash to scope events
const sessionHash = await session.getSessionHash();
session.onWillDownload(({ item }) => {
console.log("Scoped download:", item.filename);
}, sessionHash);
// Control downloads
await session.item.pause(itemId);
await session.item.resume(itemId);
await session.item.cancel(itemId);
// Clear cache
await session.clearCache();
const size = await session.getCacheSize();
console.log("Cache size:", size, "bytes");
// User agent
const ua = await session.getUserAgent();
await session.setUserAgent({
userAgent: "MyApp/1.0",
acceptLanguages: "en-US,fr,de",
});import { BrowserWindow } from "electron";
import { registerSessionModule } from "electron-main-bridge/main/session";
const win = new BrowserWindow();
// Pass the window's session instance
registerSessionModule(rpc, win.webContents.session);| Function | Return Type |
|---|---|
setApplicationMenu(template) |
Promise<void> |
getApplicationMenu() |
Promise<MenuData | null> |
buildFromTemplate(template) |
Promise<MenuData> |
sendActionToFirstResponder(action) |
Promise<void> |
popup(options) |
Promise<void> |
closePopup(windowId) |
Promise<void> |
append(menuItem, windowId) |
Promise<void> |
getMenuItemById(id, windowId) |
Promise<MenuItemData | null> |
insert(menuItem, position, windowId) |
Promise<void> |
getItems(windowId) |
Promise<MenuItemData[]> |
| Event | Data Type |
|---|---|
menu-will-show |
{ windowId?: number } |
menu-will-close |
{ windowId?: number } |
import * as menu from "electron-main-bridge/renderer/menu";
// Set application menu
await menu.setApplicationMenu([
{
label: "File",
submenu: [
{ label: "New", role: "new" },
{ label: "Open", role: "open" },
{ type: "separator" },
{ label: "Quit", role: "quit" },
],
},
{
label: "Edit",
submenu: [
{ label: "Undo", role: "undo" },
{ label: "Redo", role: "redo" },
{ type: "separator" },
{ label: "Cut", role: "cut" },
{ label: "Copy", role: "copy" },
{ label: "Paste", role: "paste" },
],
},
]);
// Clear application menu
await menu.setApplicationMenu(null);
// Get current menu
const currentMenu = await menu.getApplicationMenu();
// Show context menu
await menu.popup({
windowId: 1,
x: 100,
y: 100,
menuItems: [
{ label: "Option 1", id: "opt1" },
{ label: "Option 2", id: "opt2" },
],
});
// Listen for menu events
menu.on("menu-will-show", ({ windowId }) => {
console.log("Menu will show for window:", windowId);
});
menu.once("menu-will-close", () => {
console.log("Menu will close");
});| Function | Return Type |
|---|---|
getSystemIdleState(idleThreshold) |
Promise<string> |
getSystemIdleTime() |
Promise<number> |
getCurrentThermalState() |
Promise<string> |
isOnBatteryPower() |
Promise<boolean> |
| Event | Data Type |
|---|---|
suspend |
void |
resume |
void |
on-ac |
void |
on-battery |
void |
thermal-state-change |
{ thermalState: string } |
speed-limit-change |
{ speedLimit: number } |
shutdown |
void |
lock-screen |
void |
unlock-screen |
void |
user-did-become-active |
void |
user-did-resign-active |
void |
import * as powerMonitor from "electron-main-bridge/renderer/power-monitor";
// Get system idle state
const idleState = await powerMonitor.getSystemIdleState(60); // threshold in seconds
console.log("Idle state:", idleState); // 'active', 'idle', 'locked', 'unknown'
// Get system idle time
const idleTime = await powerMonitor.getSystemIdleTime();
console.log("Idle time:", idleTime, "seconds");
// Check if on battery power
const isOnBattery = await powerMonitor.isOnBatteryPower();
console.log("On battery:", isOnBattery);
// Get thermal state
const thermalState = await powerMonitor.getCurrentThermalState();
console.log("Thermal state:", thermalState); // 'unknown', 'nominal', 'fair', 'serious', 'critical'
// Monitor power events
powerMonitor.on("on-battery", () => {
console.log("Switched to battery power");
// Enable power-saving mode
});
powerMonitor.on("on-ac", () => {
console.log("Switched to AC power");
// Disable power-saving mode
});
powerMonitor.on("suspend", () => {
console.log("System is suspending");
// Save work
});
powerMonitor.on("resume", () => {
console.log("System resumed");
// Refresh data
});
powerMonitor.on("thermal-state-change", ({ thermalState }) => {
console.log("Thermal state changed:", thermalState);
if (thermalState === "critical") {
// Reduce workload
}
});| Function | Return Type |
|---|---|
start(type) |
Promise<number> |
stop(id) |
Promise<void> |
isStarted(id) |
Promise<boolean> |
| Type | Values |
|---|---|
PowerSaveBlockerType |
'prevent-display-sleep' | 'prevent-app-suspension' |
import * as powerSaveBlocker from "electron-main-bridge/renderer/power-save-blocker";
// Prevent display from sleeping
const displayBlockerId = await powerSaveBlocker.start("prevent-display-sleep");
console.log("Display blocker ID:", displayBlockerId);
// Prevent app from being suspended
const appBlockerId = await powerSaveBlocker.start("prevent-app-suspension");
console.log("App blocker ID:", appBlockerId);
// Check if blocker is active
const isActive = await powerSaveBlocker.isStarted(displayBlockerId);
console.log("Blocker active:", isActive);
// Stop blocker when done
await powerSaveBlocker.stop(displayBlockerId);
await powerSaveBlocker.stop(appBlockerId);
// Example: Prevent sleep during file download
async function downloadWithBlocker() {
const blockerId = await powerSaveBlocker.start("prevent-display-sleep");
try {
await startDownload();
} finally {
await powerSaveBlocker.stop(blockerId);
}
}Note
This module is macOS only.
| Function | Return Type |
|---|---|
registerForAPNSNotifications() |
Promise<boolean> |
unregisterForAPNSNotifications() |
Promise<void> |
| Event | Data Type |
|---|---|
received-apns-notification |
string (message) |
import * as pushNotifications from "electron-main-bridge/renderer/push-notifications";
// Register for APNS notifications
const success = await pushNotifications.registerForAPNSNotifications();
if (success) {
console.log("APNS registration successful");
} else {
console.log("APNS registration failed");
}
// Listen for incoming notifications
pushNotifications.on("received-apns-notification", (message) => {
console.log("Received notification:", message);
// Handle the notification
});
// Unregister when done
await pushNotifications.unregisterForAPNSNotifications();| Function | Return Type |
|---|---|
isEncryptionAvailable() |
Promise<boolean> |
encryptString(plaintext) |
Promise<Uint8Array> |
decryptString(ciphertext) |
Promise<string> |
setUsePlainTextEncryption(usePlainText) |
Promise<void> (Linux) |
getSelectedStorageBackend() |
Promise<string> (Linux) |
import * as safeStorage from "electron-main-bridge/renderer/safe-storage";
// Check if encryption is available
const isAvailable = await safeStorage.isEncryptionAvailable();
if (!isAvailable) {
console.log("Safe storage not available");
}
// Encrypt sensitive data
const plaintext = "my-secret-password";
const encrypted = await safeStorage.encryptString(plaintext);
console.log("Encrypted:", encrypted);
// Decrypt data
const decrypted = await safeStorage.decryptString(encrypted);
console.log("Decrypted:", decrypted); // "my-secret-password"
// Linux specific: set encryption method
if (process.platform === "linux") {
await safeStorage.setUsePlainTextEncryption(true);
const backend = await safeStorage.getSelectedStorageBackend();
console.log("Using backend:", backend); // 'basic_text', 'gnome_libsecret', 'kwallet'
}
// Example: Store API token securely
async function storeApiToken(token: string) {
if (!(await safeStorage.isEncryptionAvailable())) {
throw new Error("Safe storage not available");
}
const encrypted = await safeStorage.encryptString(token);
// Store encrypted token to config file
localStorage.setItem("api-token", JSON.stringify(Array.from(encrypted)));
}
async function getApiToken(): Promise<string> {
const stored = localStorage.getItem("api-token");
if (!stored) return "";
const encrypted = new Uint8Array(JSON.parse(stored));
return await safeStorage.decryptString(encrypted);
}| Function | Return Type |
|---|---|
getCursorScreenPoint() |
Promise<Point> |
getPrimaryDisplay() |
Promise<Display> |
getAllDisplays() |
Promise<Display[]> |
getDisplayNearestPoint(point) |
Promise<Display> |
getDisplayMatching(rect) |
Promise<Display> |
screenToDipPoint(point) |
Promise<Point> |
dipToScreenPoint(point) |
Promise<Point> |
screenToDipRect(rect) |
Promise<Rectangle> |
dipToScreenRect(rect) |
Promise<Rectangle> |
| Event | Data Type |
|---|---|
display-added |
Display |
display-removed |
Display |
display-metrics-changed |
Display |
import * as screen from "electron-main-bridge/renderer/screen";
// Get cursor position
const cursor = await screen.getCursorScreenPoint();
console.log("Cursor:", cursor.x, cursor.y);
// Get primary display
const primary = await screen.getPrimaryDisplay();
console.log("Primary display:", primary.size.width, "x", primary.size.height);
// Get all displays
const displays = await screen.getAllDisplays();
console.log("Number of displays:", displays.length);
// Find display nearest to cursor
const nearest = await screen.getDisplayNearestPoint(cursor);
console.log("Nearest display:", nearest.id);
// Listen for display changes
screen.on("display-added", (display) => {
console.log("Display added:", display.id);
});
screen.on("display-removed", (display) => {
console.log("Display removed:", display.id);
});
screen.on("display-metrics-changed", (display) => {
console.log("Display metrics changed:", display.id);
// Recalculate window positions if needed
});
// Convert between screen pixels and DIP points (Windows/Linux)
const dipPoint = await screen.screenToDipPoint({ x: 1920, y: 1080 });
const screenPoint = await screen.dipToScreenPoint(dipPoint);Note
This module is macOS only.
| Function | Return Type |
|---|---|
popup(sharingItem, options) |
Promise<void> |
closePopup(windowId) |
Promise<void> |
import * as shareMenu from "electron-main-bridge/renderer/share-menu";
// Show share menu
await shareMenu.popup(
{
type: "file",
files: ["/path/to/file.pdf"],
},
{
windowId: 1,
x: 100,
y: 100,
},
);
// Share URL
await shareMenu.popup({
type: "url",
url: "https://example.com",
});
// Share text
await shareMenu.popup({
type: "string",
string: "Check this out!",
});
// Close the share menu
await shareMenu.closePopup(1);| Function | Return Type |
|---|---|
openExternal(url, options) |
Promise<void> |
openPath(path) |
Promise<string> (app) |
showItemInFolder(path) |
Promise<void> |
beep() |
Promise<void> |
trashItem(path) |
Promise<void> |
writeShortcutLink(shortcutPath, operation, options) |
Promise<void> (Windows) |
readShortcutLink(shortcutPath) |
Promise<ShortcutDetails> (Windows) |
import * as shell from "electron-main-bridge/renderer/shell";
// Open URL in default browser
await shell.openExternal("https://example.com");
// Open file in default application
const app = await shell.openPath("/path/to/document.pdf");
console.log("Opened with:", app);
// Show file in file explorer
await shell.showItemInFolder("/path/to/file.txt");
// Play system beep
await shell.beep();
// Move file to trash
await shell.trashItem("/path/to/unwanted-file.txt");
// Windows specific: create shortcut
if (process.platform === "win32") {
await shell.writeShortcutLink("C:\\Users\\User\\Desktop\\MyApp.lnk", "create", {
target: "C:\\Program Files\\MyApp\\app.exe",
description: "My Application",
icon: "C:\\Program Files\\MyApp\\icon.ico",
iconIndex: 0,
});
// Read shortcut details
const details = await shell.readShortcutLink("C:\\Users\\User\\Desktop\\MyApp.lnk");
console.log("Shortcut target:", details.target);
}| Function | Return Type |
|---|---|
destroy() |
Promise<void> |
setImage(image) |
Promise<void> |
setPressedImage(image) |
Promise<void> (macOS) |
setToolTip(toolTip) |
Promise<void> |
setTitle(title) |
Promise<void> (macOS) |
getTitle() |
Promise<string> (macOS) |
setIgnoreDoubleClickEvents(ignore) |
Promise<void> (macOS) |
getIgnoreDoubleClickEvents() |
Promise<boolean> (macOS) |
setContextMenu(menuTemplate) |
Promise<void> |
displayBalloon(options) |
Promise<void> (Windows) |
removeBalloon() |
Promise<void> (Windows) |
focus() |
Promise<void> (Windows) |
getBounds() |
Promise<Rectangle> |
getGUID() |
Promise<string> (macOS/Windows) |
isDestroyed() |
Promise<boolean> |
| Event | Data Type |
|---|---|
click |
void |
right-click |
void |
double-click |
void |
balloon-show |
void (Windows) |
balloon-click |
void (Windows) |
balloon-closed |
void (Windows) |
drop-files |
{ files: string[] } |
import * as tray from "electron-main-bridge/renderer/tray";
// Set tray icon
await tray.setImage("/path/to/icon.png");
// Set tooltip
await tray.setToolTip("My Application");
// macOS specific: set title and ignore double-click
if (process.platform === "darwin") {
await tray.setTitle("1");
await tray.setIgnoreDoubleClickEvents(true);
const title = await tray.getTitle();
console.log("Tray title:", title);
}
// Set context menu
await tray.setContextMenu([
{ label: "Show App", id: "show" },
{ label: "Settings", id: "settings" },
{ type: "separator" },
{ label: "Quit", role: "quit" },
]);
// Windows specific: show balloon
if (process.platform === "win32") {
await tray.displayBalloon({
iconType: "info",
title: "Notification",
content: "You have a new message!",
});
await tray.focus();
}
// Listen for tray events
tray.on("click", () => {
console.log("Tray clicked");
// Show/hide window
});
tray.on("right-click", () => {
console.log("Right-clicked tray icon");
});
tray.on("drop-files", ({ files }) => {
console.log("Dropped files:", files);
// Process dropped files
});
// Get tray bounds
const bounds = await tray.getBounds();
console.log("Tray bounds:", bounds);
// Clean up
await tray.destroy();| Function | Return Type |
|---|---|
show(options) |
Promise<NotificationInstance> |
close() |
Promise<void> |
getTitle() |
Promise<string> |
getSubtitle() |
Promise<string> (macOS) |
getBody() |
Promise<string> |
getSilent() |
Promise<boolean> |
getReplyPlaceholder() |
Promise<string> (macOS) |
getSound() |
Promise<string> (macOS) |
getCloseButtonText() |
Promise<string> (macOS) |
getHasReply() |
Promise<boolean> (macOS) |
getUrgency() |
Promise<string> (Linux) |
getTimeoutType() |
Promise<string> (Linux/Windows) |
getActions() |
Promise<NotificationAction[]> (macOS) |
getToastXml() |
Promise<string> (Windows) |
| Event | Data Type |
|---|---|
show |
void |
click |
void |
close |
void |
reply |
{ reply: string } (macOS) |
action |
{ actionIndex: number } (macOS) |
failed |
void (Linux/Windows) |
import * as notification from "electron-main-bridge/renderer/notification";
// Show a notification
const instance = await notification.show({
title: "New Message",
body: "You have received a new message",
icon: "/path/to/icon.png",
silent: false,
});
// macOS specific: interactive notification
if (process.platform === "darwin") {
const macNotification = await notification.show({
title: "Message",
body: "Do you want to reply?",
hasReply: true,
replyPlaceholder: "Type your reply...",
actions: [
{ type: "button", text: "Reply" },
{ type: "button", text: "Ignore" },
],
closeButtonText: "Close",
});
notification.on("reply", ({ reply }) => {
console.log("User replied:", reply);
});
notification.on("action", ({ actionIndex }) => {
console.log("Action clicked:", actionIndex);
});
}
// Linux specific: urgency level
if (process.platform === "linux") {
const urgency = await notification.getUrgency();
console.log("Urgency:", urgency); // 'normal', 'critical', 'low'
}
// Listen for notification events
notification.on("show", () => {
console.log("Notification shown");
});
notification.on("click", () => {
console.log("Notification clicked");
// Focus window or perform action
});
notification.on("close", () => {
console.log("Notification closed");
});
// Windows specific: Toast XML
if (process.platform === "win32") {
const xml = await notification.getToastXml();
console.log("Toast XML:", xml);
}
// Close notification
await notification.close();| Function | Return Type |
|---|---|
isSwipeTrackingFromScrollEventsEnabled() |
Promise<boolean> |
postNotification(event, userInfo) |
Promise<void> |
postLocalNotification(event, userInfo) |
Promise<void> |
postWorkspaceNotification(event, userInfo) |
Promise<void> |
subscribeNotification(event, callback) |
Promise<void> |
subscribeLocalNotification(event, callback) |
Promise<void> |
subscribeWorkspaceNotification(event, callback) |
Promise<void> |
registerDefaults(defaults) |
Promise<void> |
getUserDefault(key, type) |
Promise<unknown> |
setUserDefault(key, value) |
Promise<void> |
removeUserDefault(key) |
Promise<void> |
getEffectiveAppearance() |
Promise<string> |
canPromptTouchID() |
Promise<boolean> |
promptTouchID(reason) |
Promise<boolean> |
isTrustedAccessibilityClient(prompt) |
Promise<boolean> |
getMediaAccessStatus(mediaType) |
Promise<string> |
askForMediaAccess(mediaType) |
Promise<string> |
| Function | Return Type |
|---|---|
getAccentColor() |
Promise<Uint8Array> (Windows) |
getColor(color) |
Promise<string> (Windows/macOS) |
getSystemColor(color) |
Promise<string> (macOS) |
| Function | Return Type |
|---|---|
getAnimationSettings() |
Promise<AnimationSettings> |
| Event | Data Type |
|---|---|
notification |
{ event: string; userInfo: unknown } |
local-notification |
{ event: string; userInfo: unknown } |
workspace-notification |
{ event: string; userInfo: unknown } |
import * as systemPreferences from "electron-main-bridge/renderer/system-preferences";
// macOS: Subscribe to notifications
if (process.platform === "darwin") {
systemPreferences.subscribeNotification("NSWindowDidMoveNotification", ({ event, userInfo }) => {
console.log("Window moved:", userInfo);
});
systemPreferences.on("notification", ({ event, userInfo }) => {
console.log("Notification:", event, userInfo);
});
// Post notifications
await systemPreferences.postLocalNotification("MyCustomNotification", {
message: "Custom event data",
});
// User defaults
await systemPreferences.registerDefaults({ mySetting: "default-value" });
const setting = await systemPreferences.getUserDefault("mySetting", "string");
await systemPreferences.setUserDefault("mySetting", "new-value");
await systemPreferences.removeUserDefault("mySetting");
// Appearance
const appearance = await systemPreferences.getEffectiveAppearance();
console.log("Appearance:", appearance); // 'dark', 'light', 'unknown'
// Touch ID
const canPromptTouchID = await systemPreferences.canPromptTouchID();
if (canPromptTouchID) {
const verified = await systemPreferences.promptTouchID("Authenticate to access sensitive data");
if (verified) {
// User authenticated
}
}
// Accessibility
const trusted = await systemPreferences.isTrustedAccessibilityClient(false);
console.log("Trusted accessibility client:", trusted);
// Media access (camera/microphone)
const cameraStatus = await systemPreferences.getMediaAccessStatus("video");
if (cameraStatus !== "granted") {
const newStatus = await systemPreferences.askForMediaAccess("video");
console.log("Camera access:", newStatus);
}
}
// Windows/macOS: Colors
if (process.platform === "win32") {
const accentColor = await systemPreferences.getAccentColor();
console.log("Accent color:", accentColor);
const windowColor = await systemPreferences.getColor("window");
console.log("Window color:", windowColor);
}
if (process.platform === "darwin") {
const systemColor = await systemPreferences.getSystemColor("windowBackgroundColor");
console.log("System color:", systemColor);
}
// Animation settings (cross-platform)
const animSettings = await systemPreferences.getAnimationSettings();
console.log("Animation settings:", animSettings);| Function | Return Type |
|---|---|
create(options) |
Promise<number> (windowId) |
close(windowId) |
Promise<void> |
destroy(windowId) |
Promise<void> |
| Function | Return Type |
|---|---|
show(windowId) |
Promise<void> |
hide(windowId) |
Promise<void> |
minimize(windowId) |
Promise<void> |
maximize(windowId) |
Promise<void> |
unmaximize(windowId) |
Promise<void> |
restore(windowId) |
Promise<void> |
setFullScreen(windowId, full) |
Promise<void> |
isFullScreen(windowId) |
Promise<boolean> |
setSimpleFullScreen(windowId, full) |
Promise<void> (macOS) |
isSimpleFullScreen(windowId) |
Promise<boolean> (macOS) |
| Function | Return Type |
|---|---|
focus(windowId) |
Promise<void> |
blur(windowId) |
Promise<void> |
isVisible(windowId) |
Promise<boolean> |
isFocused(windowId) |
Promise<boolean> |
| Function | Return Type |
|---|---|
setTitle(windowId, title) |
Promise<void> |
getTitle(windowId) |
Promise<string> |
setOpacity(windowId, opacity) |
Promise<void> |
getOpacity(windowId) |
Promise<number> |
setBackgroundColor(windowId, color) |
Promise<void> |
setSkipTaskbar(windowId, skip) |
Promise<void> |
setContentProtection(windowId, enable) |
Promise<void> |
| Function | Return Type |
|---|---|
getBounds(windowId) |
Promise<Rectangle> |
setBounds(windowId, bounds) |
Promise<void> |
getSize(windowId) |
Promise<{ width: number; height: number }> |
setSize(windowId, size) |
Promise<void> |
getPosition(windowId) |
Promise<{ x: number; y: number }> |
setPosition(windowId, position) |
Promise<void> |
center(windowId) |
Promise<void> |
| Function | Return Type |
|---|---|
setMinimumSize(windowId, size) |
Promise<void> |
getMinimumSize(windowId) |
Promise<{ width: number; height: number }> |
setMaximumSize(windowId, size) |
Promise<void> |
getMaximumSize(windowId) |
Promise<{ width: number; height: number }> |
| Function | Return Type |
|---|---|
setResizable(windowId, resizable) |
Promise<void> |
isResizable(windowId) |
Promise<boolean> |
setMovable(windowId, movable) |
Promise<void> |
isMovable(windowId) |
Promise<boolean> |
setMinimizable(windowId, minimizable) |
Promise<void> |
isMinimizable(windowId) |
Promise<boolean> |
setMaximizable(windowId, maximizable) |
Promise<void> |
isMaximizable(windowId) |
Promise<boolean> |
setClosable(windowId, closable) |
Promise<void> |
isClosable(windowId) |
Promise<boolean> |
setFocusable(windowId, focusable) |
Promise<void> |
isFocusable(windowId) |
Promise<boolean> |
setAlwaysOnTop(windowId, flag) |
Promise<void> |
isAlwaysOnTop(windowId) |
Promise<boolean> |
setKiosk(windowId, flag) |
Promise<void> |
isKiosk(windowId) |
Promise<boolean> |
| Function | Return Type |
|---|---|
setMenuBarVisibility(windowId, visible) |
Promise<void> |
isMenuBarVisible(windowId) |
Promise<boolean> |
setAutoHideMenuBar(windowId, hide) |
Promise<void> |
isMenuBarAutoHide(windowId) |
Promise<boolean> |
| Function | Return Type |
|---|---|
setProgressBar(windowId, progress, mode) |
Promise<void> |
flashFrame(windowId, flag) |
Promise<void> |
| Function | Return Type |
|---|---|
loadURL(windowId, url, options) |
Promise<void> |
loadFile(windowId, path, options) |
Promise<void> |
reload(windowId) |
Promise<void> |
executeJavaScript(windowId, code) |
Promise<unknown> |
openDevTools(windowId, options) |
Promise<void> |
closeDevTools(windowId) |
Promise<void> |
isDevToolsOpened(windowId) |
Promise<boolean> |
| Function | Return Type |
|---|---|
isMaximized(windowId) |
Promise<boolean> |
isMinimized(windowId) |
Promise<boolean> |
isDestroyed(windowId) |
Promise<boolean> |
exists(windowId) |
Promise<boolean> |
| Function | Return Type |
|---|---|
getAllWindows() |
Promise<number[]> |
getFocusedWindow() |
Promise<number | null> |
| Event | Data Type |
|---|---|
ready-to-show |
{ windowId: number } |
show |
{ windowId: number } |
hide |
{ windowId: number } |
focus |
{ windowId: number } |
blur |
{ windowId: number } |
maximize |
{ windowId: number } |
unmaximize |
{ windowId: number } |
minimize |
{ windowId: number } |
restore |
{ windowId: number } |
resize |
{ windowId: number } |
move |
{ windowId: number } |
closed |
{ windowId: number } |
page-title-updated |
{ windowId: number; title: string } |
did-finish-load |
{ windowId: number } |
did-fail-load |
{ windowId: number; errorCode: number; errorDescription: string } |
import * as browserWindow from "electron-main-bridge/renderer/browserWindow";
// Create a new window
const windowId = await browserWindow.create({
width: 1200,
height: 800,
title: "My Window",
show: false, // Don't show until ready
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
},
});
// Show window when ready
browserWindow.on(windowId, "ready-to-show", () => {
browserWindow.show(windowId);
});
// Load content
await browserWindow.loadURL(windowId, "https://example.com");
// Or load local file
// await browserWindow.loadFile(windowId, "/path/to/index.html");
// Control window
await browserWindow.setTitle(windowId, "New Title");
await browserWindow.maximize(windowId);
await browserWindow.center(windowId);
// Check window state
const isMaximized = await browserWindow.isMaximized(windowId);
const isFocused = await browserWindow.isFocused(windowId);
const title = await browserWindow.getTitle(windowId);
// Window bounds
const bounds = await browserWindow.getBounds(windowId);
console.log("Bounds:", bounds);
await browserWindow.setSize(windowId, { width: 1000, height: 700 });
await browserWindow.setPosition(windowId, { x: 100, y: 100 });
// Set minimum/maximum size
await browserWindow.setMinimumSize(windowId, { width: 600, height: 400 });
await browserWindow.setMaximumSize(windowId, { width: 1920, height: 1080 });
// Window features
await browserWindow.setResizable(windowId, false);
await browserWindow.setAlwaysOnTop(windowId, true);
await browserWindow.setProgressBar(windowId, 0.5); // 50%
// Execute JavaScript
const result = await browserWindow.executeJavaScript(windowId, "document.title");
console.log("Page title:", result);
// Listen for window events
browserWindow.on(windowId, "closed", () => {
console.log("Window closed");
});
browserWindow.on(windowId, "resize", () => {
console.log("Window resized");
});
browserWindow.on(windowId, "page-title-updated", ({ title }) => {
console.log("Page title changed to:", title);
});
browserWindow.on(windowId, "did-fail-load", ({ errorCode, errorDescription }) => {
console.error("Failed to load:", errorCode, errorDescription);
});
// Get all windows
const allWindows = await browserWindow.getAllWindows();
console.log("Open windows:", allWindows);
// Get focused window
const focused = await browserWindow.getFocusedWindow();
console.log("Focused window:", focused);
// Close window
await browserWindow.close(windowId);
// Destroy window
await browserWindow.destroy(windowId);The Electron autoUpdater module has platform-specific requirements and limitations:
-
Platform Support
- macOS: Built on Squirrel.Mac. Your application must be signed for automatic updates to work.
- Windows: Built on Squirrel.Windows. The application must be installed (not run from a portable executable) for updates to work. Use tools like
electron-winstallerorelectron-forgeto create installers. - Linux: Not supported. Use the distribution's package manager instead.
-
setFeedURL Timing
- Must be called before
checkForUpdates() - Should be set during app initialization
- Must be called before
-
Automatic Downloads
- When
checkForUpdates()finds an update, it is automatically downloaded - Calling
checkForUpdates()multiple times will download the update multiple times
- When
-
Event Sequence
checking-for-update→update-available/update-not-available- If update available →
update-downloaded(after download completes) - Call
quitAndInstall()→before-quit-for-update→ app restarts
-
Update Installation
- Calling
quitAndInstall()is optional - downloaded updates are automatically applied on next app start - Only call it after receiving the
update-downloadedevent
- Calling
Some Electron app events have special behavior when used via IPC:
-
ready/will-finish-launching- These events fire only once, usually beforeregisterAppModuleis called. Useapp.isReady()to check the current state instead. -
before-quit/will-quit- These supportevent.preventDefault()in Electron, but this cannot be done directly across IPC. If you need to prevent quit, implement a custom RPC method. -
Window events -
BrowserWindowandWebContentsobjects cannot be serialized. Only the window ID and title are forwarded. -
Platform-specific events - macOS-specific events (like
activate) are forwarded but will only fire on macOS.
Some renderer-facing APIs intentionally differ from Electron's main-process types to ensure IPC safety:
-
Menu module
MenuItemConstructorOptionsis a serializable subset.clickcallbacks are not supported.PopupOptionsuseswindowIdinstead of aBrowserWindowinstance, and does not accept acallback.setApplicationMenu(template | null)accepts a template array (ornullto clear) instead of aMenuinstance.buildFromTemplate(template)returns serializedMenuData(not aMenuinstance).
-
Session module
- Events are published for both
session.<hash>.*andsession.*. - Event payloads include optional
sessionHashso you can disambiguate multiple sessions. - Use
getSessionHash()to scope event listeners when multiple sessions are registered.
- Events are published for both
-
Tray module
right-click,double-click, andmiddle-clickevents do not include apositionin Electron. Thepositionfield is optional in this bridge.setContextMenu(template | null)accepts a menu template array (ornullto clear), not aMenuinstance.- Tray menu templates share the same
MenuItemConstructorOptionslimitations (noclickcallbacks).
The Electron globalShortcut module has important usage considerations:
-
Shortcut Conflicts
- When an accelerator is already taken by other applications,
register()will returnfalse - The OS prevents applications from fighting over global shortcuts
- Check the return value and inform users if registration fails
- When an accelerator is already taken by other applications,
-
macOS Accessibility
- On macOS 10.14 Mojave+, media key shortcuts ("Media Play/Pause", "Media Next Track", etc.) require accessibility authorization
- Request accessibility authorization before registering these shortcuts
- Use
systemPreferences.isAccessibilityEnabled()and related APIs to check status
-
Cleanup on Quit
- Call
unregisterAll()inapp.on('will-quit')to clean up shortcuts - This ensures shortcuts are released when the app quits
- Failure to do so may leave shortcuts registered even after the app closes
- Call
-
Wayland on Linux
- For Wayland sessions, add the following switch before app starts:
app.commandLine.appendSwitch("enable-features", "GlobalShortcutsPortal");
- For Wayland sessions, add the following switch before app starts:
-
Shortcut Format
- Accelerator strings follow the format:
Modifier+Key - Modifiers:
Command,CommandOrControl,Control,Alt,Option,Shift,Super - Keys:
A-Z,0-9,F1-F24, plus, minus, space, tab, etc. - Platform-specific:
Commandis macOS only,Controlis Linux/Windows,CommandOrControlworks on all platforms - Examples:
"CommandOrControl+X","Alt+F4","CommandOrControl+Shift+Z"
- Accelerator strings follow the format:
-
Event Handling
- Use
onAccelerator()to receive shortcut trigger events in the renderer - The callback receives the accelerator string that was pressed
- Remember to call the returned cleanup function when the component unmounts or you no longer need to listen
- Use
MIT