Electron’s IPC is powerful, but the ergonomics are rough. You end up wiring:
ipcMain.handle+ipcRenderer.invokefor requestsipcRenderer.onfor push events- a bunch of duplicated string names and mismatched payloads
This bridge wraps that into one small, typed layer.
- One source of truth for event names + payload types
- Typed API in the renderer:
window.electron.someAction(...) - Same API for calls and events:
- pass a payload to invoke (request/response)
- pass a callback to subscribe (push event)
- You define events in a single
events.tsfile. - Types are generated from that list.
- The preload script exposes a typed
window.electronAPI. - The main process uses a small event bus:
- if it’s an invoke call, it routes and resolves the promise
- if it’s a push event, it broadcasts to renderer windows
- No stringly‑typed mess across main/preload/renderer
- Single API for both invoke + subscribe
- Strong typing without writing extra boilerplate
- Easy to scan: all IPC surface area is in one file
- This is a convention layer, not magic. You still need to think about payloads.
- You’re opinionated about event naming (kebab‑case in config, camelCase in renderer).
- Handlers are async by default. If you want sync IPC, this isn’t for that.
- You’ll want to keep
events.tstidy as the app grows.
If you’re annoyed by Electron’s default IPC shape and want:
- a simple API in the renderer
- typed payloads without plumbing
- fewer “where does this event live?” moments