Skip to content

Easily define and use IPC functions and events in your Electron app. Fully typesafe.

License

Notifications You must be signed in to change notification settings

nlfmt/electron-bridge

Repository files navigation

Electron Bridge

Easily define and use IPC functions and events in your Electron app. Fully typesafe.
Inspired by tRPC's architecture.

Installation

npm install @nlfmt/electron-bridge
pnpm add @nlfmt/electron-bridge
yarn add @nlfmt/electron-bridge

Usage

This library is designed to be used with electron apps that enable contextIsolation, sandbox and disable nodeIntegration.

Create bridge

// bridge.ts
import { createBridge, createBridgeRouter } from '@nlfmt/electron-bridge'
  
// You can place routers in separate files and import them here
const exampleRouter = createBridgeRouter({
  log: (e, message: string) => {
    console.log("Message from IPC:", message)
  },
})

export const bridge = createBridge({
  example: exampleRouter,
})

Register bridge

// main.ts
import { bridge } from './bridge'

// This will call ipcMain.handle() under-the-hood and set up all listeners
// Don't forget to call this before you send any IPC messages
bridge.register()

Register preload script

If you have sandbox enabled, you wont be able to import registerBridgePreload, unless you are using a bundler, such as vite with the vite-plugin-electron plugin. For a workaround, see the Issues section.

// preload.ts
import { registerBridgePreload } from '@nlfmt/electron-bridge/preload'

registerBridgePreload()

Use bridge

// App.tsx
import { createRendererBridge } from '@nlfmt/electron-bridge/renderer'

// Usually would be defined in a separate file like `bridge.ts`
export const bridge = createRendererBridge()

function App() {
  return (
    <button onClick={() => bridge.example.log("Hello from App.tsx")}>
      Send message
    </button>
  )
}

export default App

Set up Events

Register Events using the withEvents method on the bridge.

// bridge.ts

type RendererEvents = {
  userClickedButton: { message: string }
}
type MainEvents = {
  ping: { data: number }
}

export const bridge = createBridge({
  example: exampleRouter,
}).withEvents<RendererEvents, MainEvents>()

// send events every second
setInterval(() => {
  bridge.emit("ping", { data: Math.random() })
}, 1000)

bridge.on()

Then, use the events api in the renderer:

// App.tsx
import { createRendererBridge } from '@nlfmt/electron-bridge/renderer'
import { useEffect } from 'react'

export const bridge = createRendererBridge()

function App() {

  useEffect(() => {
    // methods like `on` and `once` return a function that can be used to unsubscribe
    return bridge.events.on("ping", (e, data) => {
      console.log("Ping from main:", data)
    })
  }, [])
  
  return (
    <button onClick={() => bridge.example.log("Hello from App.tsx")}>
      Send message
    </button>
  )
}

Issues

Preload scripts without a bundler

If you are not using a bundler, you can use the following workaround to register everything needed by the bridge:

import { contextBridge, ipcRenderer } from "electron"

contextBridge.exposeInMainWorld("__bridge", {
  invoke: ipcRenderer.invoke,
  emit: ipcRenderer.send,
  on: (...args: Parameters<typeof ipcRenderer.on>) => {
    ipcRenderer.on(...args)
    return () => ipcRenderer.off(...args)
  },
  once: (...args: Parameters<typeof ipcRenderer.once>) => {
    ipcRenderer.once(...args)
    return () => ipcRenderer.off(...args)
  },
  off: ipcRenderer.off,
})

License

See LICENSE for more information.

About

Easily define and use IPC functions and events in your Electron app. Fully typesafe.

Topics

Resources

License

Stars

Watchers

Forks

Contributors 3

  •  
  •  
  •