Local data and native APIs
Export with a native dialog
Use Electron’s save dialog in the main process and write a user-selected JSON export file.
12 minute lesson
The renderer requests an export, but the main process owns both the native dialog and filesystem write.
Add this IPC handler:
const { dialog } = require('electron')
ipcMain.handle('notes:export', async event => {
assertTrustedSender(event.senderFrame)
const notes = await readNotes(notesPath(), validateNotes)
const result = await dialog.showSaveDialog(mainWindow, {
defaultPath: 'desktop-notes.json',
filters: [{ name: 'JSON', extensions: ['json'] }]
})
if (result.canceled || !result.filePath) return { canceled: true }
await writeNotes(result.filePath, notes)
return { canceled: false }
})
Passing mainWindow makes the dialog modal to the notes window. The operating system returns either a user-selected path or a canceled result.
Do not let the renderer provide an arbitrary export path. That would turn a narrow export feature into a general filesystem write primitive.
Read and validate the current store before exporting. This makes the exported file reflect durable notes, not a renderer value that failed validation or has not been saved.
Add an Export button that calls window.notesAPI.exportNotes() and reports whether the operation completed or was canceled.
Treat cancellation as a normal outcome, not an error. Test cancel, overwrite confirmation, an unwritable destination, and successful export. Open the resulting JSON with another program and confirm its contents.
For sensitive notes, remember that export creates a second unmanaged copy. Document that behavior and never upload it silently.
Lesson completed