@electron-toolkit/preload

Type-safe helper for exposing Electron's ipcRenderer, webFrame, webUtils, and process APIs to the renderer via contextBridge.

Library
npm
v3.0.2
214 stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum. How we score it →
64 /100 Good
Development Activity 76
Maintenance 72
Community 32
Maturity 56
Momentum 20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation. How we score it →
58 /100 Fair
Architecture 65
Code Quality 45
Innovation 40
Learning Curve 80

@electron-toolkit/preload is a small utility package for Electron preload scripts, part of the electron-toolkit monorepo. It wraps the raw ipcRenderer, webFrame, webUtils, and process APIs Electron exposes in the preload context into a single typed electronAPI object, so app authors don’t have to hand-roll a contextBridge.exposeInMainWorld call and its accompanying TypeScript declarations for every project.

Calling exposeElectronAPI() (or manually invoking contextBridge.exposeInMainWorld) makes window.electron available in the renderer with full IPC send/invoke/on/once support, CSS injection and zoom control via webFrame, file-path resolution via webUtils, and read-only process info (platform, versions, env) — working correctly whether or not context isolation is enabled.

What You Get

  • A pre-built electronAPI object bundling ipcRenderer, webFrame, webUtils, and process into one export
  • exposeElectronAPI() helper that calls contextBridge.exposeInMainWorld when context isolation is on, or falls back to a direct window assignment when it’s off
  • IPC helpers (send, invoke, sendSync, on, once, postMessage, removeListener) with on/once returning an unsubscribe function instead of requiring a separate removeListener call
  • Exported TypeScript types (ElectronAPI, IpcRenderer, IpcRendererListener, WebFrame, WebUtils, NodeProcess) for typing window.electron in the renderer
  • A version-aware guard on the deprecated sendTo API that throws a clear error on Electron 28+ instead of failing silently

Common Use Cases

  • Bootstrapping preload scripts for a new Electron app without writing a custom contextBridge wrapper
  • Adding typed IPC send/invoke/on bindings to a renderer process built with React, Vue, or vanilla JS
  • Reading file paths for drag-and-dropped files in the renderer via webUtils.getPathForFile
  • Exposing safe read-only process info (platform, versions, env) to renderer UI code that needs to branch on OS or Electron version

Under The Hood

Architecture The package is deliberately flat: a single src/index.ts builds the electronAPI object as a plain object literal whose methods delegate one-to-one to Electron’s own ipcRenderer, webFrame, and webUtils modules, alongside a getter-backed process slice exposing platform/versions/env. A companion src/types.ts mirrors Electron’s own upstream IPC/webFrame/webUtils typings as hand-written interfaces, and src/global.d.ts patches the ambient Electron.IpcRenderer type to correct the sendTo signature. The only branching logic lives in exposeElectronAPI(), which checks process.contextIsolated to decide between contextBridge.exposeInMainWorld and a direct window assignment. Because every consumer types against the exported ElectronAPI/IpcRenderer interfaces, any change to the wrapped API surface immediately shows up as a compile-time break in downstream code — the type layer functions as the de facto contract here.

Tech Stack Written in TypeScript 5.8 and built with Rollup 4 (@rollup/plugin-typescript, rollup-plugin-dts for bundling declaration files, rollup-plugin-rm for output cleaning) into dual CJS/ESM artifacts (dist/index.cjs, dist/index.mjs) wired through package.json exports. Electron itself is only a peerDependency (>=13.0.0) — the real ipcRenderer/webFrame/webUtils/contextBridge modules are supplied by the host app at runtime, not bundled. The package lives inside a pnpm-workspace monorepo sharing a root tsconfig.base.json, ESLint 9 flat config with typescript-eslint 8, Prettier 3, and simple-git-hooks + lint-staged for pre-commit formatting/linting plus a commit-message verification script.

Code Quality No test files exist anywhere under packages/preload, and the repository’s only GitHub Actions workflow (release-tag.yml) is release automation triggered by version tags — it does not run tests or a typecheck step. The safety net is TypeScript’s own type checking and the pre-commit lint/format hooks, not automated tests. Error handling is narrow and intentional rather than pervasive: sendTo throws a clear message when called on Electron 28+ (where it was removed upstream), and exposeElectronAPI() wraps contextBridge.exposeInMainWorld in a try/catch that logs to console.error; everything else is an unguarded pass-through. Method naming mirrors Electron’s own upstream API 1:1, which keeps the wrapper’s surface predictable to anyone already familiar with Electron.

What Makes It Unique The package doesn’t introduce a genuinely new technique — it’s a thin, single-purpose convenience wrapper, and equivalent hand-rolled versions are common in Electron boilerplates. Its most useful design choice is making on/once return an unsubscribe closure instead of requiring callers to retain the listener reference and call removeListener separately, a small but real ergonomic gain over Electron’s raw ipcRenderer API. The context-isolation branch inside exposeElectronAPI() similarly saves every consuming app from re-implementing the same conditional logic.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers