Electron: Error while importing electron in react | import { ipcRenderer } from 'electron'

Created on 3 Jul 2017  ·  65Comments  ·  Source: electron/electron

I have created a simple react app with create-react-app and I have integrated it with electron successfully. Everything was working great until I tried to import electron inside the action creator file. If I remove the line below, the app works fine. The problem is that I can't use the ipcRenderer to communicate from the react side to the electron main process.

This line causes the app to crash:
import { ipcRenderer } from 'electron';

I get the following error:

TypeError: fs.existsSync is not a function
(anonymous function)
node_modules/electron/index.js:6

  3 | 
  4 | var pathFile = path.join(__dirname, 'path.txt')
  5 | 
> 6 | if (fs.existsSync(pathFile)) {
  7 |   module.exports = path.join(__dirname, fs.readFileSync(pathFile, 'utf-8'))
  8 | } else {
  9 |   throw new Error('Electron failed to install correctly, please delete node_modules/electron and try installing again')

I found out on Google that this is a common problem when trying to import electron.

Thanks for the help

Most helpful comment

@MarshallOfSound my mistake.

I found the solution in issue #7300 if it can help anyone.

const { ipcRenderer } = window.require('electron');

Please note that this will work when you run the Electron app, but if you just want to test your React code inside the browser it will still crash (window.require is not defined in the browser as it is in Electron).

All 65 comments

CRA uses webpack which messes with standard module loading (including fs).

I'd recommend looking into the Electron mode for webpack and ejecting from CRA

GitHub issues are for feature requests and bug reports, questions about using Electron should be directed to the community or to the Slack Channel.

@MarshallOfSound my mistake.

I found the solution in issue #7300 if it can help anyone.

const { ipcRenderer } = window.require('electron');

Please note that this will work when you run the Electron app, but if you just want to test your React code inside the browser it will still crash (window.require is not defined in the browser as it is in Electron).

If you want to access app.quit(), you can use this:

const { app } = window.require('electron').remote;

Maybe it helps someone...

@CiriousJoker these is solutions, thanks!

I'm still getting window.require is not a function. I'm using Electron with React Starter Kit (https://github.com/kriasoft/react-starter-kit). Everything is working nicely, except this.

I've set my Electron app to load my app from the web, so the app is not running locally:
https://gist.github.com/holgersindbaek/68f6db82f507967a51ca75c527faeff6

What I'm trying to do, is call the ipcRenderer in one of my React files. I'm not sure if it's even possible when my app is being loaded from the web though. Any suggestions?

@holgersindbaek

In the same boat as you... Did you find a solution?

No. I'm pretty sure it's not possible to load the ipcRenderer from the browser.

If you are running your React app in the browser it won't work. Run it inside Electron and you should be fine.

@Amthieu Thanks for the advice. I'm still in doubt as to how I can make my React project (based on React Starter Kit) run in Electron. Any advice would be greatly appreciated:

https://discuss.atom.io/t/getting-electron-to-work-with-react-starter-kit/48594

Right, I have a solution.

1) Create a preload.js file with the code:

window.ipcRenderer = require('electron').ipcRenderer;

2) Preload this file in your main.js via webPreferences:

  mainWindow = new BrowserWindow({
    width: 800, 
    height: 600,
    webPreferences: {
      nodeIntegration: false,
      preload: __dirname + '/preload.js'
    }
  });

3) Now, you will have access from your react app. E.g. this will work:

componentDidMount() {
        if (isElectron()) {
            console.log(window.ipcRenderer);
            window.ipcRenderer.on('pong', (event, arg) => {
                this.setState({ipc: true})
            })
            window.ipcRenderer.send('ping')
        }
    }

Note - using this: https://github.com/cheton/is-electron for the isElectron() function

@HemalR Step 3 should be the following (now):

componentDidMount() {
    if (window.isElectron) {
        console.log(window.ipcRenderer);
        window.ipcRenderer.on('pong', (event, arg) => {
            this.setState({ipc: true})
        })
        window.ipcRenderer.send('ping')
    }
}

Note: window.isElectron is not a function.

@nparsons08

Apologies - should have added where I am getting isElectron from, have edited my code example with the link to: https://github.com/cheton/is-electron

@holgersindbaek
Is there a solution now

for me only work if nodeIntegration is true;

webPreferences: {
      nodeIntegration: true, 
      preload: __dirname + '/preload.js'
}

work great the @HemalR solution!

now HOW to send FROM electron TO React?

tried with
on electron side

 ipcMain.emit("pong", "Hello!"); 

but nothing got received from the React listener

window.ipcRenderer.on("pong", (event, arg) => {
        console.log("PONG");
});

is correct to use ipcMain.emit() or should I use something else?

ok just found I have to use (on the electron main process)

mainWindow.webContents.send("pong", "Hello!");

thank you to all!

I tried all of the above to no avail. What worked for me was a giant hack. Modify the file ./node_modules/electron/index.js and hard code the path to electron.exe

e.g.

function getElectronPath() {
  return 'D:\\Repos\\MyProject\\node_modules\\electron\\dist\\electron.exe';
}

module.exports = getElectronPath();

Wow, I couldn't get the IPCRenderer working on my React Components. I've tried all of the method above. Did any of you incidentally have any hints that I can use for it to be working? thanks

Hmmm... My electron app still works just fine using my solution above - but I haven't updated it in a few months now (haven't needed to).

I wonder if there is a breaking change that would stop this from working? Maybe you guys can post your electron versions?

@cyclonstep Is there any specific error you were getting? Hard to help without a code snippet or some logs...

I am using parcel for bundling.
Window.require also solved it for me (also showing, what didn't):

import Vue from 'vue/dist/vue.min'
import App from './App'

// BAD? import { ipcRenderer } from 'electron'
// BAD? const { ipcRenderer } = require('electron')
// GOOD:
const { ipcRenderer } = window.require('electron')

( further below in the same file is the electron „pong-Demo“, which kinda prooves, it works)

Perhaps noteworthy: Even when doing it wrong, bundle size does not grow (compare to without the electron-require. This is so far my first&so far only render-side electron import) by the entire Electron size or such, but only by around 20kb, which seems to be some shim/wrapper code on its own, coming from node_modules/electron-download/node_modules/debug/dist/debug.js:242:ff...

2: [function (require, module, exports) {
  // shim for using process in browser
  var process = module.exports = {}; // cached from whatever global is present so that test runners that
 stub it

Anyway, things work as said above.

Node version 10.2.0
Chrome version 66.0.3359.181
Electron version 3.0.2

window.require wasn't working for me in my main script with error window is not defined, so I switched to const electron = eval('require')("electron"). Hope this helps someone. Using webpack, and problem was that webpack was evaluating my require statement at compilation time.

@MarshallOfSound my mistake.

I found the solution in issue #7300 if it can help anyone.

const { ipcRenderer } = window.require('electron');

Please note that this will work when you run the Electron app, but if you just want to test your React code inside the browser it will still crash (window.require is not defined in the browser as it is in Electron).

And for typescript:

import {IpcRenderer} from 'electron';

declare global {
  interface Window {
    require: (module: 'electron') => {
      ipcRenderer: IpcRenderer
    };
  }
}

const { ipcRenderer } = window.require('electron');

@moshfeu Your solution works fantastic. I don't need Webpack or Browserfy to use IpcRenderer in my React project. Thanks so much again :D

For typescript and using @HemalR 's example from above but WITHOUT nodeIntegration: true: https://github.com/electron/electron/issues/9920#issuecomment-336757899:

Right, I have a solution.

  1. Create a preload.js file with the code:
window.ipcRenderer = require('electron').ipcRenderer;
  1. Preload this file in your main.js via webPreferences:
  mainWindow = new BrowserWindow({
    width: 800, 
    height: 600,
    webPreferences: {
      nodeIntegration: false,
      preload: __dirname + '/preload.js'
    }
  });
  1. Now, you will have access from your react app. E.g. this will work:
componentDidMount() {
      if (isElectron()) {
          console.log(window.ipcRenderer);
          window.ipcRenderer.on('pong', (event, arg) => {
              this.setState({ipc: true})
          })
          window.ipcRenderer.send('ping')
      }
  }

Note - using this: https://github.com/cheton/is-electron for the isElectron() function

combined with
https://github.com/electron/electron/issues/9920#issuecomment-447157348

I used this:

import { IpcRenderer } from 'electron';

declare global {
  interface Window {
    ipcRenderer: IpcRenderer
  }
}

export const { ipcRenderer } = window;

Hope that helps someone out there! Works with stenciljs, and I imagine react and angular

just add target: "electron-renderer" in webpack configs.
export default {
...
target: "electron-renderer"
...
}

Hi, I use CRA + Electron. And I build my project by target 'electron-renderer'. It works well while loading files in build folder, but it throws error when I develop and load url localhost:3000. I guess the reason is that I use node api and electron api in react components. Anybody has the solution ? Thanks.

TypeError: fs.existsSync is not a function

Yah, the fs api is not available in the browser. If I understand the situation correctly, you run your app using react-scripts start (which is the default script of npm start in CRA).

The right way to do this is to run electron .. You can see it in the docs: https://electronjs.org/docs/tutorial/first-app.

LMK if it works for you. You can also see how it works in my app - https://github.com/moshfeu/y2mp3 (Notice that it wasn't create with CRA)

Hi, I use CRA + Electron. And I build my project by target 'electron-renderer'. It works well while loading files in build folder, but it throws error when I develop and load url localhost:3000. I guess the reason is that I use node api and electron api in react components. Anybody has the solution ? Thanks.

TypeError: fs.existsSync is not a function

just add target: "electron-renderer" in webpack configs.
export default {
...
target: "electron-renderer"
...
}

It works for me too.

Here is my tools version:

  • webpack: "3.8.1"
  • electron: "4.2.1"

I think electron-renderer just for solve this electron problem.I don't need to use wired syntax like window.required, and it even lost typing!

For me worked setting target: "electron-rendering" for webpack and nodeIntegration: true in BrowserWindow options
I'm using webpack+electron+react of today's last versions

I am running create-react-app with TypeScript an Electron. I followed these very good instructions for a setup. But then I also ran into this error. The solution that works for me is the sum of the things said in this thread:

  1. Make react-script use "electron-renderer" as target. I use rescripts with rescript-env for this.

package.json

  "rescripts": [
    "env"
  ],

.rescriptsrc.js

module.exports = [require.resolve("./webpack.config.js")];

webpack.config.js:

module.exports = config => {
  config.target = "electron-renderer";
  return config;
};
  1. Add nodeIntegration
new BrowserWindow({
  webPreferences: {
    nodeIntegration: true
  }
});
  1. Add src/typings.d.ts:
declare var window: Window;
interface Window {
  require: any;
}

And theeeen finally in your app

App.tsx

const { remote } = window.require("electron");
console.log(remote.getCurrentWindow());

@LukasBombach
This should work too:

declare var window: Window;
interface Window {
  require: NodeRequire
}

Then you will have typing on the required consts

Thanks!

Here are the steps I used for Vue in-case it's helpful to someone else.

Ensure node integration is enabled for the renderer process by adding it to the web preferences when creating a browser window:

new BrowserWindow({
    webPreferences: {
        nodeIntegration: true,
    },
})
````

Configure webpack to package your application for electron renderer by adding a target to your `vue.config.js` (or wherever you set your vue settings). 
```js
module.exports = {
    configureWebpack: {
        target: 'electron-renderer',
    },
}

Import what you need within your application. I'm importing the shell module into my component like so:

import { shell } from 'electron'

To anyone still having the same problem. This is the best solution i have found so far

```js
new BrowserWindow({
webPreferences: {
nodeIntegration: true
}
});

I hope that this comment get noticed, because a lot of people are asking about importing fs or ipcRenderer in your apps. It's a common-need for electron apps but I found not many people have got it right, and are using outdated patterns. tl;dr - there is a security vulnerability if you don't import your node module (ie. fs) or electron module (ie. ipcRenderer) in the correct way. If you are using your app for only yourself you are _probably_ safe, but if you ever want to share or sell your app you should read ahead.

Our goal

Before I go into the solution, it's important to understand _why_ we are doing this in the first place. Electron apps allow us to include node modules in our apps, which gives them amazing power, but security concerns. We want to allow our app to use native-os (ie. node) features, but we don't want them to be abused.

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

The easy way

Setting nodeIntegration: true in your BrowserWindow gives your renderer process access to node modules. Doing _this_, is vulnerable. You have access to require("fs") and require("electron"), but that means if someone were to find a XSS vulnerability, they could run any command you've exposed in your renderer process.

Think deleting all of the files on your computer, or something else that's really bad.

The (alternative) easy way

Alongside setting nodeIntegration to true, it's likely that your app is using webpack to bundle application files. Webpack messes up with certain symbols, so settings like target: 'electron-renderer' or webpack externals allows you to pass through these variables (ipcRenderer) into your app instead.

Still, this changes nothing except how you are setting up your app.

The (other-alternative) easy way

You can use the remote module which gives you access to ipcRenderer. It's basically 'The easy way' in a different form. It's not recommended by Electron's security recommendations to do this since this type of attack suffers from a prototype pollution vector.

Ie. using remote could allow someone to modify a js-object's prototype and wreck havoc on your machine/app.

The _almost_ right way

@marksyzm has a better solution, although not perfect, where we use IPC to send the ipcRenderer to the renderer process. This type of setup is also vulnerable to prototype pollution attacks. If you want to get your app 80% of the way there, I'd use this method, as it probably won't require you to do much refactoring.

The right way

The right way of importing your fs/ipcRenderer into your renderer process is with IPC (inter-process-communication). This is Electron's way of allowing you to talk between main and renderer process. Broken down, this is how your app needs to look:

  1. A BrowserWindow has a preload property. This property is a js file that loads with access to require (which means you can require ipcRenderer)
  2. Your BrowserWindow will also have contextIsolation: true to prevent prototype pollution attacks, but this means you need to use the contextBridge to pass the ipcRenderer to your renderer process
  3. Using the preload script and the contextBridge, you allow your renderer process to access the ipcRenderer
  4. In your main script, you create listeners for the ipcRenderer (in the ipcMain module). Within these listeners you can use the fs module

_Roughly_ this is what all these steps look like:

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>

At the very least, I _believe_ you need electron v7 for these features.

How do I know this?

I care about secure electron apps, and built secure-electron-template in order to create an electron application template to bake-in security instead of thinking of security as an afterthought.

Building on @reZach's comment above, I'm doing something like the following. The main difference is in preload.js, where my API is closer to that of ipcRenderer.

main.js

let newWindow = null;
function createWindow() {
    newWindow = new BrowserWindow({
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
            enableRemoteModule: false,
            preload: path.join(__dirname, "preload.js")
        }
    });

    newWindow.webContents.on('did-finish-load', () => {
        newWindow.webContents.send('APP_MY_INIT', { data: 'hi' });
    });
}

ipcMain.on('APP_SOMETHING', (event, ...args) => {
    // ...
});

ipcMain.handle('APP_SOMETHING_ELSE', (event, ...args) => {
    // ...
    return myVar;
});

preload.js

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

function callIpcRenderer(method, channel, ...args) {
    if (typeof channel !== 'string' || !channel.startsWith('APP_')) {
        throw 'Error: IPC channel name not allowed';
    }
    if (['invoke', 'send'].includes(method)) {
        return ipcRenderer[method](channel, ...args);
    }
    if ('on' === method) {
        const listener = args[0];
        if (!listener) throw 'Listener must be provided';

        // Wrap the given listener in a new function to avoid exposing
        // the `event` arg to our renderer.
        const wrappedListener = (_event, ...a) => listener(...a);
        ipcRenderer.on(channel, wrappedListener);

        // The returned function must not return anything (and NOT
        // return the value from `removeListener()`) to avoid exposing ipcRenderer.
        return () => { ipcRenderer.removeListener(channel, wrappedListener); };
    }
}

contextBridge.exposeInMainWorld(
    'myIpcRenderer', {
        invoke: (...args) => callIpcRenderer('invoke', ...args),
        send: (...args) => callIpcRenderer('send', ...args),
        on: (...args) => callIpcRenderer('on', ...args),
    },
);

client.js

const { myIpcRenderer } = window;

const removeMyListener = myIpcRenderer.on('APP_MY_INIT', data => {
    console.log(data);
    myIpcRenderer.send('APP_SOMETHING', 'foo');
})

async function test() {
    const result = await myIpcRenderer.invoke('APP_SOMETHING_ELSE', 'foo', 'bar');
    console.log(result);
}
test();

if (/* should remove listener === true */) {
    removeMyListener();
}

And for those using TypeScript, types.d.ts

declare global {
    interface Window {
        myIpcRenderer: MyIpcRenderer,
    }
}

export interface MyIpcRenderer {
    invoke(channel: string, ...args: any[]): Promise<any>;
    send(channel: string, ...args: any[]): void;

    /** @return A function that removes this listener. */
    on(channel: string, listener: (...args: any[]) => void): () => void;
}

Does anyone know why I am getting module not found error when I try to put this in preload.js?

const errorLogging = require('../renderer/utils/errorLogging');

Thanks

@silentlight this isn't related to electron, do you perhaps have a github repo of your own where the conversation can continue? (It looks to be your path in require() is not correct and that's why its throwing an error, without seeing more code).

Although there is already the window.require solution, I am gonna share mine, just for fun:

function magic(module){
    require(module)
}

const { ipcRenderer } = magic('electron')

Your bundlers won't identify as you are importing a module since you are not using ES modules or CommonJS syntax, so it won't try to bundle it and so won't throw an error

I wonder why nobody has tried this yet 😆

@marc2332 true, but then you directly expose yourself to potential attacks by this magic wrapper. It may not apply to your scenario, but definitely a concern you should consider.

@reZach I see no reasons why the window.require is more secure to be honest. Btw, there are dirtier ways of doing it.

const { ipcRenderer } = eval("require('electron')")

@marc2332 neither are secure. I'm just noting, if you use remote assets, you are opening yourself open to injection attacks this way. 😄

Thanks reZach! I think people will come to understand your approach sooner

or later

Mark Elphinstone
www.oxfordsourceltd.com

Hi Zach, does the contextBridge solution support prototypes?

My original code with contextIsolation: false simply attached a function on window. This function returns a Promise<MediaStream> however the docs of contextBridge says that prototypes are dropped and when I used the contextBridge I receive an empty object.

Is there a way to support prototypes but also ensuring contextIsolation: true?

Hi Zach, does the contextBridge solution support prototypes?

My original code with contextIsolation: false simply attached a function on window. This function returns a Promise<MediaStream> however the docs of contextBridge says that prototypes are dropped and when I used the contextBridge I receive an empty object.

Is there a way to support prototypes but also ensuring contextIsolation: true?

No, it doesn't. I was trying to find a comment I remember by one of the team members that this is intended behavior but I couldn't. The best I can say is in Electron v8, (pr 20214) brought about changes to the serialization of objects within IPC (effectively when context isolation is on) that will drop support (currently deprecated, will be removed in future versions) for functions or prototypes.

NOTE: Objects that aren't serializable with V8's Structured Clone algorithm, such as functions, DOM objects, special Node/Electron objects like process.env or WebContents, or any objects containing such items will be serialized with the old base::Value-based algorithm. However, this behavior is deprecated and will throw an exception beginning with Electron 9.

This probably isn't the news you wanted to hear but I hope I helped.

Hi Zach, does the contextBridge solution support prototypes?
My original code with contextIsolation: false simply attached a function on window. This function returns a Promise<MediaStream> however the docs of contextBridge says that prototypes are dropped and when I used the contextBridge I receive an empty object.
Is there a way to support prototypes but also ensuring contextIsolation: true?

No, it doesn't. I was trying to find a comment I remember by one of the team members that this is intended behavior but I couldn't. The best I can say is in Electron v8, (pr 20214) brought about changes to the serialization of objects within IPC (effectively when context isolation is on) that will drop support (currently deprecated, will be removed in future versions) for functions or prototypes.

NOTE: Objects that aren't serializable with V8's Structured Clone algorithm, such as functions, DOM objects, special Node/Electron objects like process.env or WebContents, or any objects containing such items will be serialized with the old base::Value-based algorithm. However, this behavior is deprecated and will throw an exception beginning with Electron 9.

This probably isn't the news you wanted to hear but I hope I helped.

That's unfortunate, but thanks for your quick reply.

I've followed the outlined steps above (given by @reZach and @aplum), and I'm passing information from my client.js to the preload.js to main.js.

When running everything locally, communication works, but when I run electron-builder, the communication breaks down. For instance, using setBadge() works wonderfully when it's run locally. I can take the count from the client.js file and see it being passed into the contextBridge and then into main.js and successfully setting the app.dock.setBadge(count). Is there something I'm missing with the communication in Electron after it's been built with electron-builder?.

"electron": "^8.2.3",
"electron-builder": "^22.4.0",

Main.js

const { ipcMain } = electron;

app.on('ready', () => {
    mainWindow = new BrowserWindow({
        height: height,
        width: width,
        minHeight: 575,
        minWidth: 900,
        center: true,
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
            enableRemoteModule: false,
            spellcheck: true,
            preload: path.join(__dirname, "preload.js")
        },
    });
}

ipcMain.on('SEND_BADGE_COUNT', (e, count) => {
    const badgeCount = count > 0 ? count : '';
    app.dock.setBadge(`${badgeCount}`)
})

Preload.js

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

contextBridge.exposeInMainWorld(
    'setBadgeCountForElectron', {
        send: (channel, data) => {
            console.log(`Preload ${channel}, ${data}`)
            ipcRenderer.send(channel, data)
        }
    }
)

Client.js

const { setBadgeCountForElectron } = window;

function sendBadgeCount(count) {
  !!setBadgeCountForElectron && setBadgeCountForElectron.send('SEND_BADGE_COUNT', count)
}

sendBadgeCount(count)

package.json

  "name": "desktop_app",
  "version": "0.1.8-beta",
  "private": true,
  "description": "",
  "homepage": "./",
  "main": "public/electron.js",
  "build": {
    "copyright": "Copyright © 2020 My Company",
    "appId": "com.my-app.app",
    "productName": "My Company",
    "buildVersion": "0.0.1-beta",
    "mac": {
      "category": "public.app-category.utilities",
      "icon": "./public/images/mac-icon.png"
    },
    "win": {
      "icon": "./public/images/windows-icon.png"
    },
    "files": [
      "build/**/*",
      "node_modules/**/*"
    ],
    "directories": {
      "buildResources": "assets"
    }
  },

@SterlingChin , I'm not a maintainer with the electron team, but this might be a better question-suited for the electron-builder repo.

I'm more inclined to think it's an issue with the code rather than electron-builder when I took a moment to think about it.

I'm more inclined to think it's an issue with the code rather than electron-builder when I took a moment to think about it.

I'm sure I'm missing some key piece that would make this all work as expected. Thank you for your response. I've opened an issue in the electron-builder repo, but it has not received any traction.

I'm more inclined to think it's an issue with the code rather than electron-builder when I took a moment to think about it.

I'm sure I'm missing some key piece that would make this all work as expected. Thank you for your response. I've opened an issue in the electron-builder repo, but it has not received any traction.

Have you created an MVP repo with your problem? That sometimes helps and in the process you realize what's going on.

@Amthieu and @CiriousJoker i love you both thank you.

  1. yarn add -D customize-cra react-app-rewired
  2. modify package.json to use react-app-rewired start app: "start": "BROWSER=none react-app-rewired start",
  3. create file 'config-overrides' in react app root directory:
    `const { override } = require('customize-cra');

function addRendererTarget(config) {
config.target = 'electron-renderer';

return config;
}

module.exports = override(addRendererTarget);`

  1. you could import { ipcRenderer} from 'electron' now :)

Im using vuejs and just add the code in vue.config.js

module.exports = {
"transpileDependencies": [
"vuetify"
],
pluginOptions: {
electronBuilder: {
nodeIntegration: true
}
}
}

@genilsonmm Holy crap that worked! I've been going crazy for the past 3 hours. Thanks!!
The specific piece of that that worked for me was the
```javascript
pluginOptions: {
electronBuilder: {
nodeIntegration: true
}
},

"transpileDependencies": [
"vuetify"
],

that is not working on me...but window.require('electron') is working well

For all the people that have the problem "window.require is not a function"

You have tu create a preoload script on electron.

  1. Create a file named preload.js on the directory where you have de electron main script, Put this code on it:

    window.require = require;

  2. Go to your electron main script and type this in the code where you create the window:

    win = new BrowserWindow({
    width: 1280,
    height: 720,
    webPreferences: {
    nodeIntegration: false,
    preload: __dirname + '/preload.js'
    },

    })
    With this you will preload the script before all, this fixed the problem for me. I hope also for you :)

@reZach your solution worked for me, but I noticed a mistake that other people might stumble over (because I did, woops!):

        receive: (channel, func) => {
            let validChannels = ["fromMain"];
            if (validChannels.includes(channel)) {
                // Deliberately strip event as it includes `sender` 
                ipcRenderer.on(channel, (event, ...args) => fn(...args));
            }
        }

you define your callback function as "func" , but then call "fn", if you change this, it works precisely as you describe it.
Big Thank you for the detailed post :+1:

@genilsonmm If I put vue.config.js :

pluginOptions: {
electronBuilder: {
nodeIntegration: true,

I get error "require is not defined" :

image

If I comment the line "nodeIntegration: true" the error message disappears and the app works.

What does it mean?

@marcoippolito I believe it means that the webpack bundled code is trying to use require which is node js's module resolution method. Since this is not available if you disable node integration then the code cannot execute. What you need to do is modify your webpack config to target browsers (var is the target if I remember correctly) and ensure that none of your code is using node apis and the error should go away.

For those using TypeScript there is a good example in the official Next.js examples:
https://github.com/vercel/next.js/search?q=ipcRenderer&unscoped_q=ipcRenderer

preload.ts

/* eslint-disable @typescript-eslint/no-namespace */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { ipcRenderer, IpcRenderer } from 'electron'

declare global {
  namespace NodeJS {
    interface Global {
      ipcRenderer: IpcRenderer
    }
  }
}

// Since we disabled nodeIntegration we can reintroduce
// needed node functionality here
process.once('loaded', () => {
  global.ipcRenderer = ipcRenderer
})

index.tsx

import { useState, useEffect } from 'react'

const Home = () => {
  const [input, setInput] = useState('')
  const [message, setMessage] = useState(null)

  useEffect(() => {
    const handleMessage = (event, message) => setMessage(message)
    global.ipcRenderer.on('message', handleMessage)

    return () => {
      global.ipcRenderer.removeListener('message', handleMessage)
    }
  }, [])

  const handleSubmit = (event) => {
    event.preventDefault()
    global.ipcRenderer.send('message', input)
    setMessage(null)
  }

  return (
    <div>
      <h1>Hello Electron!</h1>
    </div>
  )
}

export default Home

@genilsonmm Holy crap that worked! I've been going crazy for the past 3 hours. Thanks!!
The specific piece of that that worked for me was the

   pluginOptions: {
        electronBuilder: {
            nodeIntegration: true
        }
    },

正解,标记

I have this issue too with NodeIntegration enabled. Both window.require and require don't work.

+This only happens with react, not plain electron

Was this page helpful?
0 / 5 - 0 ratings