Preload and IPC
Expose a preload API
Use contextBridge to publish one renderer method for each allowed notes operation.
10 minute lesson
The preload script translates renderer intentions into fixed IPC channels. Replace src/preload.js with this bridge:
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('notesAPI', {
loadNotes: () => ipcRenderer.invoke('notes:load'),
saveNotes: notes => ipcRenderer.invoke('notes:save', notes),
exportNotes: () => ipcRenderer.invoke('notes:export'),
onNewNote: callback =>
ipcRenderer.on('notes:new', () => callback())
})
The renderer will receive window.notesAPI. It does not receive the ipcRenderer object.
Notice how the event wrapper drops Electron’s event object before calling renderer code. The callback receives no privileged sender reference.
Do not expose ipcRenderer directly. Current Electron releases prevent transferring the complete object over contextBridge, and a generic wrapper would recreate the same security problem.
Bridge values are copied or proxied across isolated JavaScript worlds. Pass plain serializable data. DOM nodes, Electron objects, and functions nested inside note data cannot cross IPC as application records.
Each method name describes one capability. This API is easy to audit and can stay stable even if the IPC channel names change.
Open the renderer console and inspect window.notesAPI. Confirm that loadNotes exists while require, raw channel selection, and filesystem methods do not.
For a long-lived page that registers listeners repeatedly, add a matching unsubscribe operation. Desktop Notes registers onNewNote once during startup, so one listener matches the page lifetime.
Lesson completed