Docs

Usage

This page covers how window.Electron is built and consumed, the error-handling pattern every call follows, the React hooks, and how to extend the bridge with your own IPC channels. For the full grouped method list, jump to Reference: API surface at the bottom.

The window.Electron bridge

electron/preload.ts (scaffolded by dae init) builds the bridge and exposes it:

import { contextBridge, ipcRenderer } from 'electron';
import { createElectronBridge } from '@devioarts/electron/preload';

contextBridge.exposeInMainWorld('Electron', createElectronBridge());

From renderer code, every method is a typed, promise-based call over IPC:

window.Electron.minimize();
const version = await window.Electron.getAppVersion();
const platform = await window.Electron.getPlatform(); // 'darwin' | 'win32' | 'linux' | ...

Methods take positional arguments (clipboard.writeText(text)), not a single options object — this is deliberate, and plugins follow the same convention (see Plugins).

Throwing vs window.Electron.safe — two flavors of every call

Every wrapped bridge method exists in two forms side by side: the default window.Electron.<ns>.<method>(), which throws an IpcError on failure, and window.Electron.safe.<ns>.<method>(), which never throws and instead resolves an IpcResult<T>{ ok: true, data } on success, { ok: false, error: { code, message } } on failure. Both call the exact same main-process handler over the exact same IPC channel; only what happens to a failure differs. Nothing needs to be configured to use .safe — it's just a safe property already sitting on window.Electron.

// Throwing default — reach for this when a failure is exceptional
import { isIpcError } from '@devioarts/electron';

try {
  await window.Electron.autoLaunch.setEnabled(true);
} catch (e) {
  if (isIpcError(e) && e.code === 'UNSUPPORTED_OS') return; // expected on Linux
  throw e;
}

// .safe mirror — reach for this when a failure is a normal branch to check inline
const result = await window.Electron.safe.autoLaunch.setEnabled(true);
if (!result.ok) {
  if (result.error.code === 'UNSUPPORTED_OS') return; // expected on Linux
  console.error(result.error.message);
}

code is one of VALIDATION, NOT_CONFIGURED, RUNTIME, UNSUPPORTED_OS, FORBIDDEN, UNKNOWN — see API reference: Error codes for what each one means.

Not every method has a .safe twin: pure event subscriptions (onDeepLink, onShortcut, onMenuAction, onPowerMonitorEvent, onScreenEvent, onAppStateChange, onElectronError) and fire-and-forget calls (updater.quitAndInstall()) can't fail in a way worth reporting, so there's nothing to unwrap either way. Some namespaces mix request/response methods with an on() subscriber — only the request/response methods get a .safe twin there. API reference: Per-namespace .safe coverage has the full breakdown, namespace by namespace.

The playground demonstrates the throwing style, centralized in playground/src/helpers/ipc.ts's unwrap() helper — logs the failure and returns undefined on a throw, otherwise returns the resolved value.

React hooks (@devioarts/electron/react)

Thin hooks over window.Electron — no new IPC, no new capability, just ergonomics: typed access and auto-cleanup event subscriptions.

  • useElectron(): ElectronBridge — typed access to window.Electron. Throws if used outside Electron (no bridge present).
  • useElectronEvent<T>(subscribe, callback) — subscribes to any window.Electron on* event for the component's lifetime; the (callback) => unsubscribe pattern is wrapped in useEffect so the listener is removed automatically on unmount.
  • useNativeTheme(): NativeThemeSnapshot | null — live native theme state, subscribes to nativeTheme.onUpdated.
  • useShortcut(): string | null — latest global-shortcut event name fired since mount.
  • useMenuAction(): MenuActionEvent | null — latest native menu action (app/context/dock/tray) fired since mount.
  • useScreenEvent(): ScreenEventPayload | null — latest display/screen change event since mount.
import { useElectron, useNativeTheme, useElectronEvent } from '@devioarts/electron/react';

function TitleBar() {
  const electron = useElectron();
  const theme = useNativeTheme();

  useElectronEvent(window.Electron.onShortcut, ({ event }) => {
    if (event === 'open-search') setSearchOpen(true);
  });

  return (
    <div>
      <button onClick={() => electron.minimize()}>Minimize</button>
      <span>{theme?.shouldUseDarkColors ? 'dark' : 'light'}</span>
    </div>
  );
}

Task examples

// Native OS notification (Notification Center / Action Center / libnotify) —
// not the web Notification API, so it works even when the window isn't focused.
const id = await window.Electron.notifications.show({ title: 'Export finished' });
const unsubscribe = window.Electron.notifications.on((event) => {
  if (event.id === id && event.type === 'click') showExportFolder();
});

// Clipboard images — plain text copy/paste is already covered by the
// standard navigator.clipboard Web API, no bridge needed for that.
const pngDataUrl = await window.Electron.clipboard.readImage();

// Run a pre-configured Node.js helper script (see configuration/processes.md) —
// the renderer only ever names it by alias, never by file path.
const { id: taskId } = await window.Electron.utilityProcess.start('imageResizer');
window.Electron.utilityProcess.onMessage(({ id, message }) => {
  if (id === taskId) console.log('progress from worker:', message);
});

// Taskbar/Dock progress indicator — e.g. during a long download or export.
await window.Electron.setProgressBar(0.42);
await window.Electron.setProgressBar(-1); // remove it

// Native OS drag of a file out of the window (e.g. onto the desktop or another
// app) — must be called from the renderer's own `dragstart` handler, and only
// works while that drag gesture is actually in progress.
someElement.addEventListener('dragstart', () => {
  void window.Electron.startFileDrag({ file: '/path/to/export.pdf', icon: '/path/to/icon.png' });
});

Extending the bridge with your own channels

Spread createElectronBridge()'s result alongside your own properties before exposing:

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

contextBridge.exposeInMainWorld('Electron', {
  ...createElectronBridge(),
  myCustomMethod: () => ipcRenderer.invoke('myApp:doSomething'),
});

Register the matching handler in electron/main.ts with the same trusted-origin check every built-in channel uses:

import { trustedIpcHandle } from '@devioarts/electron/main';

trustedIpcHandle('myApp:doSomething', (event, arg) => {
  // ...
});

Then extend the type in electron-env.d.ts (scaffolded by dae init) so window.Electron.myCustomMethod is typed:

declare module '@devioarts/electron' {
  interface ElectronBridge {
    myCustomMethod: () => Promise<void>;
  }
}

For a reusable capability shared across projects, prefer a plugin over ad-hoc additions like this.

Reference: API surface

Every module below has its own page with the full method table (signatures, params, return types), events, and exactly which methods have a .safe twin — see API reference for the overview and the .safe coverage table. This page stays focused on task-level usage; jump to a module page once you know which one you need:

Module Bridge surface
System & window Flat: quit, minimize, ..., getAppVersion, getDeviceId, setBadgeCount, setProgressBar, startFileDrag, onAppStateChange
Managed windows windows.*
Updater updater.*
Dialogs dialogs.*
Secure storage secureStorage.*
Protocols protocols.*
Session session.*
Downloads downloads.*
Print print.*
Desktop capture desktopCapture.*
Auto launch autoLaunch.*
Native theme nativeTheme.*
External commands externalCommands.*
Clipboard clipboard.*
Notifications notifications.*
System preferences systemPreferences.*
Crash reporter crashReporter.*
Utility process utilityProcess.*
Power Flat: getPowerMonitorIdleState, ..., onPowerMonitorEvent, startPowerSaveBlocker, ...
Screen Flat: getAllDisplays, ..., onScreenEvent
Shortcuts Flat: registerShortcut, unregisterShortcut, onShortcut
Menu Flat: showContextMenu, onMenuAction
Deep linking Flat: onDeepLink
Errors Flat: onElectronError
Plugins: Built-in plugins Filesystem.*, Preferences.*, Network.*, FileViewer.*, PrivacyScreen.*

Next: Configuration to enable the opt-in features these methods unlock, or Plugins to add your own capability group in the same style.

Last updated on July 18, 2026