I'm creating an Electron app for my own purpose. My problem is when I'm using node functions inside my HTML page it throws an error of:
'require()' is not defined.
Is there any way to use Node functionalities in all my HTML pages? If it is possible please give me an example of how to do this or provide a link. Here are the variables I'm trying to use in my HTML page:
var app = require('electron').remote;
var dialog = app.dialog;
var fs = require('fs');
and these are the values I'm using in all my HTML windows within Electron.
As of version 5, the default for nodeIntegration changed from true to false.
You can enable it when creating the Browser Window:
app.on('ready', () => {
mainWindow = new BrowserWindow({
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
}
});
});
Edit 2022
I've published a larger post on the history of Electron and it's security that provides additional context on the changes that affect how security was approached in different framework versions (and what's the best approach to take).
Original answer
I hope this answer gets some attention, because a large majority of answers here leave large security holes in your electron app. In fact this answer is essentially what you should be doing to use require() in your electron apps. (There is just a new electron API that makes it a little bit cleaner in v7).
I wrote a detailed explanation/solution in github using the most current electron apis of how you can require() something, but I'll explain briefly here why you should follow an approach using a preload script, contextBridge and ipc.
The problem
Electron apps are great because we get to use node, but this power is a double-edged sword. If we are not careful, we give someone access to node through our app, and with node a bad actor can corrupt your machine or delete your operating system files (among other things, I imagine).
As brought up by #raddevus in a comment, this is necessary when loading remote content. If your electron app is entirely offline/local, then you are probably okay simply turning on nodeIntegration:true. I still would, however, opt to keep nodeIntegration:false to act as a safeguard for accidental/malicious users using your app, and prevent any possible malware that might ever get installed on your machine from interacting with your electron app and using the nodeIntegration:true attack vector (incredibly rare, but could happen)!
What does the problem look like
This problem manifests when you (any one of the below):
Have nodeIntegration:true enabled
Use the remote module
All of these problems give uninterrupted access to node from your renderer process. If your renderer process is ever hijacked, you can consider all is lost.
What our solution is
The solution is to not give the renderer direct access to node (ie. require()), but to give our electron main process access to require, and anytime our renderer process needs to use require, marshal a request to the main process.
The way this works in the latest versions (7+) of Electron is on the renderer side we set up ipcRenderer bindings, and on the main side we set up ipcMain bindings. In the ipcMain bindings we set up listener methods that use modules we require(). This is fine and well because our main process can require all it wants.
We use the contextBridge to pass the ipcRenderer bindings to our app code (to use), and so when our app needs to use the required modules in main, it sends a message via IPC (inter-process-communication) and the main process runs some code, and we then send a message back with our result.
Roughly, here's what you want to do.
main.js
const {
app,
BrowserWindow,
ipcMain
} = require("electron");
const path = require("path");
const fs = require("fs");
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win;
async function createWindow() {
// Create the browser window.
win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: false, // is default value after Electron v5
contextIsolation: true, // protect against prototype pollution
enableRemoteModule: false, // turn off remote
preload: path.join(__dirname, "preload.js") // use a preload script
}
});
// Load app
win.loadFile(path.join(__dirname, "dist/index.html"));
// rest of code..
}
app.on("ready", createWindow);
ipcMain.on("toMain", (event, args) => {
fs.readFile("path/to/file", (error, data) => {
// Do something with file contents
// Send result back to renderer process
win.webContents.send("fromMain", responseObj);
});
});
preload.js
const {
contextBridge,
ipcRenderer
} = require("electron");
// Expose protected methods that allow the renderer process to use
// the ipcRenderer without exposing the entire object
contextBridge.exposeInMainWorld(
"api", {
send: (channel, data) => {
// whitelist channels
let validChannels = ["toMain"];
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, data);
}
},
receive: (channel, func) => {
let validChannels = ["fromMain"];
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`
ipcRenderer.on(channel, (event, ...args) => func(...args));
}
}
}
);
index.html
<!doctype html>
<html lang="en-US">
<head>
<meta charset="utf-8"/>
<title>Title</title>
</head>
<body>
<script>
window.api.receive("fromMain", (data) => {
console.log(`Received ${data} from main process`);
});
window.api.send("toMain", "some data");
</script>
</body>
</html>
Disclaimer
I'm the author of secure-electron-template, a secure template to build electron apps. I care about this topic, and have been working on this for a few weeks (at this point in time).
For security reasons, you should keep nodeIntegration: false and use a preload script to expose just what you need from Node/Electron API to the renderer process (view) via window variable. From the Electron docs:
Preload scripts continue to have access to require and other Node.js features
Example
main.js
const mainWindow = new BrowserWindow({
webPreferences: {
preload: path.join(app.getAppPath(), 'preload.js')
}
})
preload.js
const { remote } = require('electron');
let currWindow = remote.BrowserWindow.getFocusedWindow();
window.closeCurrentWindow = function(){
currWindow.close();
}
renderer.js
let closebtn = document.getElementById('closebtn');
closebtn.addEventListener('click', (e) => {
e.preventDefault();
window.closeCurrentWindow();
});
First off, #Sathiraumesh solution leaves your electron application with huge security issue. Imagine that your app is adding some extra features to messenger.com, for example toolbar's icon will change or blink when you've have unread message. So in your main.js file, you create new BrowserWindow like so (notice I intentionally misspelled messenger.com):
app.on('ready', () => {
const mainWindow = new BrowserWindow({
webPreferences: {
nodeIntegration: true
}
});
mainWindow.loadURL(`https://messengre.com`);
});
What if messengre.com is a malicious website, that wants to harm your computer. If you set nodeIntegration: true this site has access to your local file system and can execute this:
require('child_process').exec('rm -r ~/');
And your home directory is gone.
Solution
Expose only what you need, instead of everything. This is achived by preloading javascript code with require statements.
// main.js
app.on('ready', () => {
const mainWindow = new BrowserWindow({
webPreferences: {
preload: `${__dirname}/preload.js`
}
});
mainWindow.loadURL(`https://messengre.com`);
});
// preload.js
window.ipcRenderer = require('electron').ipcRenderer;
// index.html
<script>
window.ipcRenderer.send('channel', data);
</script>
Now awful messengre.com cannot delete your entire file system.
It looks like Electron's security evolved like this (source).
Electron 1 nodeIntegration defaults to true
Renderer has full access to Node API -- huge security risks if Renderer loads remote code.
Electron 5 nodeIntegration defaults to false
When set to false, a preload script is used to expose specific API to Renderer. (The preload script always has access to Node APIs regardless of the value of nodeIntegration)
//preload.js
window.api = {
deleteFile: f => require('fs').unlink(f)
}
Electron 5 contextIsolation defaults to true (actually still defaults to false in Electron 11)
This causes preload script to run in a separate context. You can no longer do window.api = .... You now have to do:
//preload.js
const { contextBridge } = require('electron')
contextBridge.exposeInMainWorld('api', {
deleteFile: f => require('fs').unlink(f)
})
Electron 6 require()ing node builtins in sandboxed renderers no longer implicitly loads the remote version
If Renderer has sandbox set to true, you have to do:
//preload.js
const { contextBridge, remote } = require('electron')
contextBridge.exposeInMainWorld('api', {
deleteFile: f => remote.require('fs').unlink(f)
})
Electron 10 enableRemoteModule default to false (remote module deprecated in Electron 12)
The remote module is used when you need to access Node APIs from a sandboxed Renderer (as in above example); or when you need to access Electron APIs that are available only to the Main process (such as dialog, menu). Without remote, you'll need to write explicit IPC handlers like follows.
//preload.js
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('api', {
displayMessage: text => ipcRenderer.invoke("displayMessage", text)
})
//main.js
const { ipcMain, dialog } = require('electron')
ipcMain.handle("displayMessage", text => dialog.showMessageBox(text))
Electron 10 deprecate nodeIntegration flag (removed in Electron 12)
Recommendation
Always set {nodeIntegration: false, contextIsolation: true, enableRemoteModule: false}.
For max security, set {sandbox: true}. Your preload script will have to use IPC to call the Main process to do everything.
If sandbox is false, your preload script can access Node API directly, as in require('fs').readFile. You're secure as long as you don't this:
//bad
contextBridge.exposeInMainWorld('api', {
readFile: require('fs').readFile
})
Are you using nodeIntegration: false while BrowserWindow initialization? If so, set it to true (defaults value is true).
And include your external scripts in the HTML like this (not as <script> src="./index.js" </script>):
<script>
require('./index.js')
</script>
All I wanted to do was to require a js file in my html page because of the tutorial I was following. However, I intend to use remote modules so security was paramount. I modified Michael's answer up there so I'm posting, purely for those who spent hours looking for a secure alternative to 'require' like me. If the code is incorrect, feel free to point it out.
main.js
const electron = require('electron');
const app=electron.app;
const BrowserWindow=electron.BrowserWindow;
const ipcMain=electron.ipcMain;
const path=require('path');
const url=require('url');
let win;
function createWindow(){
win=new BrowserWindow({
webPreferences:{
contextIsolation: true,
preload: path.join(__dirname, "preload.js")
}
});
win.loadURL(url.format({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file',
slashes: true
}));
win.on('close', function(){
win=null
});
}
app.on('ready', createWindow);
preload.js
const electron=require('electron');
const contextBridge=electron.contextBridge;
contextBridge.exposeInMainWorld(
"api", {
loadscript(filename){
require(filename);
}
}
);
index.html
<!DOCTYPE html>
<html>
<head>
<title>Hello World App</title>
</head>
<body>
<h1>Hello World</h1>
<button id="btn">Click</button>
</body>
<script>
window.api.loadscript('./index.js');
</script>
</html>
index.js
const btn = document.getElementById('btn');
btn.addEventListener('click', function(){
console.log('button clicked');
});
I am especially curious to know if this still presents a security risk. Thanks.
If you just don't care about any security issues and want to have require being interpreted correctly by JavaScript on the browser window, then have an extra flag on the main.js code:
webPreferences: {
nodeIntegration: true,
nodeIntegrationInWorker: true,
nodeIntegrationInSubFrames: true,
enableRemoteModule: true,
contextIsolation: false //required flag
}
//rest of the code...
You have to enable the nodeIntegration in webPreferences to use it. see below,
const { BrowserWindow } = require('electron')
let win = new BrowserWindow({
webPreferences: {
nodeIntegration: true
}
})
win.show()
There was a breaking api changes in electron 5.0(Announcement on Repository). In recent versions nodeIntegration is by default set to false.
Docs Due to the Node.js integration of Electron, there are some extra symbols inserted into the DOM like module, exports, require. This causes problems for some libraries since they want to insert the symbols with the same names.To solve this, you can turn off node integration in Electron:
But if you want to keep the abilities to use Node.js and Electron APIs, you have to rename the symbols in the page before including other libraries:
<head>
<script>
window.nodeRequire = require;
delete window.require;
delete window.exports;
delete window.module;
</script>
<script type="text/javascript" src="jquery.js"></script>
</head>
For sake of actuality and completeness I am adding my piece of cake. Here is what I find important about this topic. Please keep in mind the date of this post - October 2022, the version of electron is 21.1.1.
There is an article in electron docs called Inter-Process Communication where this topic is described in a very clear way.
The following code is just a copy of the example code on that aforementioned site.
The main.js file:
const {app, BrowserWindow, ipcMain} = require('electron')
const path = require('path')
function createWindow () {
const mainWindow = new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
ipcMain.on('set-title', (event, title) => {
const webContents = event.sender
const win = BrowserWindow.fromWebContents(webContents)
win.setTitle(title)
})
mainWindow.loadFile('index.html')
}
app.whenReady().then(() => {
createWindow()
app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit()
})
The takeaway:
in webPreferences define only the preload script and leave all those nodeIntegration, nodeIntegrationInWorker, nodeIntegrationInSubFrames, enableRemoteModule, contextIsolation apply the defaults.
The next file is preload.js:
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
setTitle: (title) => ipcRenderer.send('set-title', title)
})
Here the electronAPI object will be injected into the browsers context so there will be a window.electronAPI object which will have a member function called setTitle. Of course you can add whatever other properties there.
The setTitle function only calls ipcRenderer.send which is one end of the Inter-Process Communication brigde or tunnel if you like.
What you send in here falls out on the other end, which is in the main.js file, the ipcMain.on function. Here you register for the set-title event.
The example continues with the index.html file:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
<title>Hello World!</title>
</head>
<body>
Title: <input id="title"/>
<button id="btn" type="button">Set</button>
<script src="./renderer.js"></script>
</body>
</html>
which loads the renderer.js script:
const setButton = document.getElementById('btn')
const titleInput = document.getElementById('title')
setButton.addEventListener('click', () => {
const title = titleInput.value
window.electronAPI.setTitle(title)
});
and there you can access the window.electronAPI.setTitle function, which you defined in preload.js where it sends the title into ipcRenderer and this title then falls out of ipcMain in main.js fireing an event and causing a function to run which in turn sets the application title.
So once again I want to emphasize to read the documentation. There is more about IPC with exanples. Also read the Context Isolation chapter, it is short and very clear.
Finally, I made it work.Add this code to your HTML document Script Element.
Sorry for the late Reply.I use the below code to do this thing.
window.nodeRequire = require;
delete window.require;
delete window.exports;
delete window.module;
And use nodeRequire instead of using require.
It works Fine.
Failed to load resource: net::ERR_FILE_NOT_FOUND
I am using latest version of npm, node.js and electron.
My html file calls upon a terrautils.js:
<script type="module" src="./terrautils.js"></script>
My terrautils.js file has first line as:
import { LCDClient, Coin } from './node_modules/#terra-money/terra.js';
Which is the link to an npm module that I want to use, and I have it installed and have confirmed that the folder is really there using file explorer. I also know the module works perfectly file, because this issue only happens when I run using npm start but when I run using node terrautils.js and swap from using import to using require, it works perfectly fine.
This is something to do with electron I think, and not sure what to do. My main.js has createWindow function like:
function createWindow (site) {
var win = new BrowserWindow({
width: 700,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: true,
contextIsolation: true,
devTools: true,
}
})
win.loadFile(site)
}
I do not care about security, I just want this to work. Thank you.
Solution:
Change html invoking of the js file to this instead:
<script>
require('./terrautils.js')
</script>
dont know how it works, but it does.
Webview tag that is present in the renderer process, somewhere in <body>:
<webview src="http://somewebpage.com" preload="somescript.js">
somescript.js is executed in somewebpage, but if somewebpage has <iframe>s in it, the script will not run in the iframe.
How can I make it run? And before any other script in the iframe?
I found this issue on github that seems related:
https://github.com/electron/electron/pull/19260
but it doesn't make any sense...
I tried adding nodeintegrationinsubframes and changing values from false to true
<webview src="somewebpage" preload="somescript.js" nodeintegrationinsubframes="false">
but it has no effect :(
main.js
mainWindow = new BrowserWindow({
width: 1024,
height: 728,
webPreferences: {
nodeIntegrationInSubFrames: true,
webviewTag: true,
nodeIntegration: true
}
});
renderer
<webview
src="https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_iframe"
preload="./preload.js"
style='width: 100%; height: 800px'
nodeIntegrationInSubFrames
/>
preload.js
process.once("loaded", () => {
alert(window.location);
});
You can specify where you are going to execute javascript based on the window.location This above code will show the locations of every sub iframes.
This works for me very well.
I was having the same problems with my project and updating the electron to the latest beta version solved for me.
I assume you know how to do this:
npm install electron#11.0.0-beta.6
You still have to consider the stability concerns of using a development version of the package.
By scratching on electron's documentation perhaps this could be helpful as an alternative.
app.js
let win
app.whenReady().then(() => {
win = new BrowserWindow({
webPreferences: {
nodeIntegrationInSubFrames: true,
webviewTag: true,
nodeIntegration: false,
preload: path.join(app.getAppPath(), 'preload.js') // 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.
}
})
...
})
renderrer
<webview
src="https://somewebpage.com"
preload="./preload.js"
nodeintegrationinsubframes>
preload.js
/* It can be used by the preload script to add removed Node global symbols back to the global scope when node integration is turned off */
const _setImmediate = setImmediate
const _clearImmediate = clearImmediate
process.once('loaded', () => {
global.setImmediate = _setImmediate
global.clearImmediate = _clearImmediate
})
My answer is based on the following resources:
Electron Documentation: Tag
Electron Documentation:
BrowserWindow
Electron Documentation: process
Electron
Documentation: Web embeds in Electron - WebViews
Electron
Documentation: Preload Example
Im trying to build an electron app and want to use window.require. Unfortunately the compiler says "TypeError: window.require is not a function". Ironically require works only in main.js.
Here the code Im trying to run:
const electron = window.require('electron')
const low = window.require('lowdb')
const FileSync = window.require('lowdb/adapters/FileSync')
I read in another post that somebody have had the same problem and it was fixed by adding this code into the .html file:
<script type="text/javascript" src="../../../Gehaltseinstellungen_Hinzufügen.js">
window.nodeRequire = require;
delete window.require;
delete window.exports;
delete window.module;
</script>
Also the author said using "nodeRequire" instead of require would solve the problem but it doesn't...
Another option I read about is that the NodeIntegration is set to false while the rendering process is activated, but I don't know how to activate Node while rendering.
you can set the webPreferences.contextIsolation to be false like this
webPreferences: {
nodeIntegration: true,
contextIsolation: false
}
it maybe works
It is unclear what version of Electron you are using. The syntax you are using is non-standard.
First – if you are using Electron 5.0, nodeIntegration is false by default in BrowserWindows so you need to specify it explicitly when you create your window:
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
Given the above, the syntax below works fine (i.e. no 'window' reference needed):
const { ipcRenderer, remote } = require('electron');
Needed all 3 setup like so to make this work:
webPreferences: {
nodeIntegration: true,
enableRemoteModule: true,
contextIsolation: false,
},
note: macbook m1, big sur 11.4, maybe it has to do something about OS, idk.
P.s. As mentioned in comments, before using nodeIntegration - check the Electron docs to understand when it may be a security issue:
Isolation For Untrusted Content A security issue exists whenever you
receive code from an untrusted source (e.g. a remote server) and
execute it locally. As an example, consider a remote website being
displayed inside a default BrowserWindow. If an attacker somehow
manages to change said content (either by attacking the source
directly, or by sitting between your app and the actual destination),
they will be able to execute native code on the user's machine.
⚠️ Under no circumstances should you load and execute remote code with
Node.js integration enabled. Instead, use only local files (packaged
together with your application) to execute Node.js code. To display
remote content, use the tag or BrowserView, make sure to
disable the nodeIntegration and enable contextIsolation.
Source: https://www.electronjs.org/docs/latest/tutorial/security#isolation-for-untrusted-content
I also met this issue in Electron + Angular.
webPreferences: {
nodeIntegration: true,
contextIsolation: false
}
Configuration above works for me.
Same issue in Electron + React + Typescript. This solved it for me
webPreferences: {
nodeIntegration: true,
enableRemoteModule: true,
contextIsolation: false
}
I was stuck with this problem for a couple of days.
Could not figure out for the life of me.
Went through a lot of docs and stackoverflow and finally!!!!
I fixed this error the following way :
create a file preload.js :
const { remote } = require('electron');
window.ipcRenderer = require('electron').ipcRenderer;
then in main.js/electron.js:
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true,
//this line is crucial
preload: path.join(app.getAppPath(), '/path-to-file/preload.js'),
contextIsolation: false
}
})
and finally in App.js:
const { ipcRenderer } = window
I got ipcRenderer in window from electron.
I'm pretty sure you can get anything else that you would want.
You don't need to modify web preferences as has been suggested, and I would recommend not doing it unless you have a better reason to because of the implied security issues (https://www.electronjs.org/docs/latest/tutorial/security#isolation-for-untrusted-content).
Instead, what you can do is use the preload script to add any functionality you need to window. Like this for example:
preload.js (located in the root folder):
contextBridge.exposeInMainWorld('ipcRenderer', {
invoke: (event) => ipcRenderer.invoke(event),
});
webPreferences in main.js:
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
}
Now in your code you can reference it:
const { ipcRenderer } = window as any;
const response = await ipcRenderer.invoke(...);
Reference documentation here: https://www.electronjs.org/docs/latest/tutorial/tutorial-preload#communicating-between-processes
I am developing an electron app. All good and nice until I wanted to use IPC from the renderer to call some native features. I understand that adding the following line to my Webpack config would allow me to import electron on the renderer side.
module.exports = {
// ...
target: 'electron-renderer',
}
I get the following error when adding this line
Uncaught ReferenceError: require is not defined
And the offending line is
module.exports = require("querystring");
Which sort of makes sense, since the browser does not understand "requires".
Note that without the electron-renderer target the application works well, except I cannot do things like
import {ipcRenderer} from 'electron';
Any thoughts what I could be doing wrong? Thank you!
Just recently ran into this. One thing to look out for is to ensure nodeIntegration is set to true when creating your renderer windows.
mainWindow = new electron.BrowserWindow({
width: width,
height: height,
webPreferences: {
nodeIntegration: true
}
});
Also faced this issue, new answer:
mainWindow = new electron.BrowserWindow({
width: width,
height: height,
webPreferences: {
nodeIntegration: true,
contextIsolation: false
}
});
AFAIU the recommended way is to use contextBridge module (in the preload.js script). It allows you to keep the context isolation enabled but safely expose your APIs to the context the website is running in.
https://www.electronjs.org/docs/latest/tutorial/context-isolation
Following this way, I also found that it was no longer necessary to specify target property in the Webpack config.