Preload and IPC
Handle two-way IPC
Pair ipcRenderer.invoke with ipcMain.handle when the renderer needs a result from the main process.
10 minute lesson
Loading notes is a request with one asynchronous response. Pair ipcRenderer.invoke() with ipcMain.handle().
Register each handler once in the main process:
const { app, BrowserWindow, ipcMain } = require('electron')
ipcMain.handle('notes:load', event => {
assertTrustedSender(event.senderFrame)
return []
})
app.whenReady().then(() => {
createWindow()
})
Call the handler from the renderer:
async function start() {
notes = await window.notesAPI.loadNotes()
renderNotes()
}
start()
invoke() returns a promise. The value returned by the main handler becomes the resolved renderer value.
The main process must validate the sender before returning data or performing privileged work. We will add data validation next.
Use one channel for one request shape. Do not create a generic notes:command handler with an operation name and arbitrary payload. Separate handlers make validation, authorization, and error behavior visible.
Prefer this asynchronous pattern to synchronous IPC. A synchronous renderer request blocks the renderer while the main process works and can freeze both sides through dependency cycles.
Thrown handler errors reject the renderer promise, but Electron does not preserve arbitrary error details across IPC. Return user-safe messages and keep internal diagnostics in the main process.
Call loadNotes() twice and inspect the main-process log. Each invocation should reach the same one registered handler, not duplicate listeners created every time a window opens.
Lesson completed