The Problem
Since Electron 5 nodeIntegration
is disabled by default in the window. Since normal browser API does not know require
or join
, you get errors when you try.
Reenabling nodeIntegration
You could enable nodeIntegration
again, but it was disabled for a reason. Be sure you read and understand the electron security tutorial.
Using a preload script
Another way is to use a preload script. Let's have a look at the BrowserWindow
documentation.
When creating a new BrowserWindow you can add several options. For this case we need the webPreferences.preload
option:
Specifies a script that will be loaded before other scripts run in the page. This script will always have access to node APIs no matter whether node integration is turned on or off. The value should be the absolute file path to the script. When node integration is turned off, the preload script can reintroduce Node global symbols back to the global scope.
Be aware that the preload script is run in the renderer process.
Example
Following is an example app, that opens a window with a button that uses the electron dialog
to select files. This would not work with disabled nodeIntegration
but thanks to our preload script, we reintroduced dialog.showOpenDialog()
to our window.
main.js
const { app, BrowserWindow } = require("electron");
const { join } = require("path");
let win;
app.on("ready", () => {
win = new BrowserWindow({
webPreferences: {
//this is the default since electron 5
nodeIntegration: false,
//here you load your preload script
preload: join(__dirname, "preload.js")
}
});
win.loadURL(join(__dirname, "index.html"));
});
preload.js
const { dialog } = require("electron").remote;
window.mystuff = {
selectFile
};
async function selectFile() {
const files = await dialog.showOpenDialog({
properties: ["openFile", "multiSelections"]
});
return files;
}
index.html
<html>
<body>
<main>
<button onclick="myFunction()">select file</button>
<ul id="foo"></ul>
</main>
<script>
async function myFunction() {
//the function provided by the preload script
const files = await window.mystuff.selectFile();
const list = document.getElementById("foo");
for (const file of files) {
const node = document.createElement("LI");
const textNode = document.createTextNode(file);
node.appendChild(textNode);
list.appendChild(node);
}
}
</script>
</body>
</html>
Sending events via IPC
If you are unsure your functionality should be exposed in the window, you can also send events via ipcRenderer
.
preload.js
const { ipcRenderer } = require("electron");
window.mystuff = {
selectFile
};
function selectFile() {
return new Promise(resolve => {
ipcRenderer.on("selected-files", (e, files) => {
resolve(files);
});
ipcRenderer.send("select-files");
});
}
Additional part in main.js
ipcMain.on("select-files", async () => {
const files = await dialog.showOpenDialog({
properties: ["openFile", "multiSelections"]
});
win.webContents.send("selected-files", files);
});