English
@quiteer/electron-preload
One of the Electron runtime packages maintained in the electronup repository, designed to work with electronup. Source: packages/electron-preload.
Preload script for Electron: import it to get the script path, plug it into webPreferences.preload, and expose the curated $ipc / $clipboard / $webFrame APIs to the renderer.
It saves you from writing contextBridge boilerplate while staying safer than nodeIntegration — the exposed surface is fixed and contains no require.
Install
bash
pnpm add @quiteer/electron-preloadQuick start
The default export is the absolute path of the preload script; pass it to preload directly:
ts
import { BrowserWindow } from 'electron'
import preloadPath from '@quiteer/electron-preload'
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
sandbox: true,
preload: preloadPath,
},
})The same works in a CommonJS main process:
js
const preloadPath = require('@quiteer/electron-preload')Use it in the renderer:
ts
await window.$clipboard.writeText('hello')
const text = await window.$clipboard.readText()
window.$ipc.send('some-channel', { id: 1 })
window.$webFrame.setZoomFactor(1.2)Exposed APIs
$ipc
| Method | Description |
|---|---|
send(channel, ...args) | One-way send |
sendSync(channel, ...args) | Synchronous send; blocks the renderer, prefer invoke unless necessary |
invoke(channel, ...args) | Two-way call, returns a Promise |
on(channel, listener) | Listen to main process pushes |
once(channel, listener) | Listen once |
removeAllListeners(channel?) | Remove listeners; clears all channels when omitted |
$clipboard
All methods are asynchronous (Promise), matching recent Electron versions:
| Method | Description |
|---|---|
clear(type?) | Clear; type only applies on Linux, defaults to clipboard |
has(mimetype) | Whether the format exists |
read() | Read everything, returns ClipboardItem[] |
readText() | Read text |
write(data) | Write ClipboardItem[] |
writeText(text) | Write text |
$webFrame
| Method | Description |
|---|---|
setZoomFactor(factor) / getZoomFactor() | Zoom factor; 1 is the original size |
setZoomLevel(level) / getZoomLevel() | Zoom level |
insertText(text) | Insert text into the focused element |
executeJavaScript(code, userGesture?) | Execute script, returns a Promise |
executeJavaScriptInIsolatedWorld(...) | Execute in an isolated world |
setIsolatedWorldInfo(...) | Configure an isolated world |
getResourceUsage() | Resource usage |
clearCache() | Clear cache |
getFrameForSelector(selector) | Get a frame by selector |
firstChild() / nextSibling() / opener() / parent() | Related frames, null when absent |
routingId() | Routing id of the current frame |
On the native WebFrame, firstChild / nextSibling / opener / parent are properties and routingId is a method. Here they are all methods, because WebFrame instances cannot cross contextBridge — only plain values can.
Type hints
Declare the globals in the renderer for completion:
ts
// global.d.ts
interface Window {
$ipc: import('@quiteer/electron-preload').PreloadIpc
$clipboard: import('@quiteer/electron-preload').PreloadClipboard
$webFrame: import('@quiteer/electron-preload').PreloadWebFrame
}When used with @quiteer/electron-ipc, merge its channel types too:
ts
interface Window {
$ipc: import('@quiteer/electron-preload').PreloadIpc & import('@quiteer/electron-ipc/web').ExpandPreloadIpc
}Notes
- The output is CommonJS. Preload scripts cannot be ESM when
sandboxis enabled, so this package always emits.cjs; do not import it as ESM. - Everything crosses
contextBridge, so arguments and return values must be serializable — functions,Symbols and class instances cannot pass through. This is whyexecuteJavaScripttakes no callback. - The exposed surface is intentionally small. Add your own
ipcMainchannels for more capabilities instead of disablingsandbox. - The script path lives inside the installed package. When packaging, make sure resources under
node_modulesship with the main process output (or handle it with--extraResourceand similar).