Solving 'require() is not defined' in Electron: Security Best Practices and Implementation

Nov 23, 2025 · Programming · 12 views · 7.8

Keywords: Electron | Node.js | Security Practices | require() | Preload Scripts

Abstract: This technical article addresses the common 'require() is not defined' error encountered when using Node.js modules in Electron applications. It explores the security implications of enabling nodeIntegration, provides step-by-step implementation of preload scripts with contextBridge and IPC communication, and offers comprehensive code examples for secure Electron development. The article balances functionality with security considerations for modern Electron applications.

Problem Background and Error Analysis

When developing Electron applications, developers often need to use Node.js modules within renderer processes (HTML pages). However, attempting to call the require() function in HTML pages frequently results in the "'require()' is not defined" error. This typically occurs in scenarios like:

var app = require('electron').remote;
var dialog = app.dialog;
var fs = require('fs');

The root cause of this issue lies in Electron's evolving security policies. Starting from Electron version 5, the default value of nodeIntegration changed from true to false, meaning renderer processes no longer have direct access to the Node.js environment by default.

Basic Solution: Enabling nodeIntegration

The most straightforward approach is to re-enable the nodeIntegration option. This can be achieved by configuring webPreferences when creating the BrowserWindow instance:

app.on('ready', () => {
    mainWindow = new BrowserWindow({
        webPreferences: {
            nodeIntegration: true,
            contextIsolation: false,
        }
    });
});

This method's advantage lies in its simplicity, granting renderer processes full access to the Node.js environment, including all functionalities like require(), fs module, and more. However, this convenience comes with significant security implications.

Security Risk Analysis

Enabling nodeIntegration: true introduces serious security vulnerabilities to applications. When renderer processes have complete Node.js access, if page content becomes compromised through injection or hijacking, attackers can execute arbitrary system commands through the renderer process, including dangerous operations like file deletion and malware installation.

Risks are particularly pronounced in these scenarios:

Even for entirely local applications, maintaining nodeIntegration: false serves as an important security barrier against potential malware exploiting this attack vector.

Recommended Secure Solution

To maintain functionality while ensuring security, the recommended approach involves using preload scripts combined with IPC communication. This method's core principle isolates Node.js functionality within the main process, providing necessary functional interfaces to renderer processes through secure communication channels.

Main Process Configuration

In the main process file, we configure BrowserWindow to use preload scripts with appropriate security settings:

const { app, BrowserWindow, ipcMain } = require("electron");
const path = require("path");
const fs = require("fs");

let win;

async function createWindow() {
    win = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
            enableRemoteModule: false,
            preload: path.join(__dirname, "preload.js")
        }
    });

    win.loadFile(path.join(__dirname, "dist/index.html"));
}

app.on("ready", createWindow);

ipcMain.on("toMain", (event, args) => {
    fs.readFile("path/to/file", (error, data) => {
        win.webContents.send("fromMain", responseObj);
    });
});

Preload Script Implementation

Preload scripts execute in the renderer process context but maintain access to the Node.js environment. We use contextBridge to safely expose APIs to the renderer process:

const { contextBridge, ipcRenderer } = require("electron");

contextBridge.exposeInMainWorld(
    "api", {
        send: (channel, data) => {
            let validChannels = ["toMain"];
            if (validChannels.includes(channel)) {
                ipcRenderer.send(channel, data);
            }
        },
        receive: (channel, func) => {
            let validChannels = ["fromMain"];
            if (validChannels.includes(channel)) {
                ipcRenderer.on(channel, (event, ...args) => func(...args));
            }
        }
    }
);

Renderer Process Usage

In HTML pages, we can securely communicate with the main process through the exposed API:

<!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>

Security Best Practices Summary

When developing Electron applications, adhere to these security principles:

Conclusion

Multiple approaches exist for resolving the "require() is not defined" error in Electron, ranging from simple nodeIntegration enabling to more complex but secure preload script solutions. While enabling nodeIntegration provides the most direct solution, production environments should prioritize security by adopting architectures based on preload scripts and IPC communication. This approach, though adding development complexity, effectively prevents potential security threats and ensures long-term application stability.

Developers should choose appropriate solutions based on specific application requirements and security needs. For internal tools or completely offline applications, simpler solutions may be considered; for applications handling user data or connecting to the internet, stricter security measures are essential.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.