electron: Requiring electron outside of main.js causes a TypeError

  • Electron version: 1.3.5
  • Operating system: Mint 17

Hello

I am using Electron with React.js and babelify, among other plugins (list at the bottom). Attempting to use require('electron'), for example to gain access to electron’s BrowserWindow, results in the console showing the following error: index.js:4 Uncaught TypeError: fs.readFileSync is not a function

I can, however, use const electron = require('electron'); in main.js. For what it’s worth, I am also using watchify to pack everything into a js bundle.

Here is the complete list of dependencies in my package.json:

"devDependencies": {
        "axios": "^0.9.1",
        "babel-preset-es2015": "^6.6.0",
        "babel-preset-react": "^6.5.0",
        "babelify": "^7.2.0",
        "classnames": "^2.2.3",
        "electron": "^1.3.5",
        "electron-reload": "^0.2.0",
        "jquery": "^2.2.3",
        "react": "^0.14.8",
        "react-autocomplete": "^1.0.0-rc2",
        "react-dom": "^0.14.7",
        "react-sound": "^0.4.0",
        "soundmanager2": "^2.97.20150601-a"
    },

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 52
  • Comments: 93 (6 by maintainers)

Commits related to this issue

Most upvoted comments

For anyone encountering this problem in the future and reading this thread, using window.require instead of require is one possibility of avoiding the conflict between electron’s and browserify’s require function.

window.require did solve the issue of fs.existsSync is not a function but it lead to another error : window.require is not a function. How shall I solve it?

FWIW, I ran into the same issue trying to use electron in the renderer process with create-react-app that uses webpack in the backend instead of browserify. window.require seems to solve it for me too though I’m not entirely clear why.

Edit: I figured out why 😃 We want to require electron during runtime from the nodejs environment provided at the runtime rather than the nodejs environment used during compilation by webpack. By default, globals are bound to window and webpack compilation ignores the window global - hence window.require works.

Another way to tell webpack to ignore a global name is to use a comment like /*global Android*/ in the JS file. In another project using CRA built app in an Android WebView, I used the above to get access to a Java object exposed to JS through the JavaScript interface provided by Webview.

For anyone still stuck on this: you’ll get the error window.require is not a function unless you explicitly declare nodeIntergation as truewhen you declare your BrowserWindow:

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

yep fixed my create react app / webpack issue. change

import electron, { ipcRenderer } from 'electron'

to

const electron = window.require("electron")

@srinathh how are you exposing/loading your CRA app as renderer in your main? are you building first (and modifying the html static resource paths)

@steric85, I faced the window.require is not a function in typescript, I managed to fix it this way:

declare global {
  interface Window {
    require: any;
  }
}

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

One reason some people are still experiencing this problem is that nodeIntegration has been removed from Electron v12. So setting nodeIntegration: true has no affect. Instead, you need to set contextIsolation: false. I suggest reading the justification to understand why and becoming familiar with the contextBridge to make it work safely.

With Create-React-App 2, you can use the craco npm module:

https://www.npmjs.com/package/@craco/craco

Change react-scripts by craco in your package.json

craco.config.js

module.exports = {
    webpack: {
        configure: {
            target: 'electron-renderer'
        }
    }
};

And in you’ll have access to your Electron context like that:

import Electron from 'electron';
const { dialog } = Electron.remote;

@nukeop How are you launching your app. Your `start script should look like.

"scripts": {
  "start": "electron ."
}

I have a feeling you are attempting to run your main.js with node

node main.js

Which won’t work

@steric85 It is because you running the app in webpage environment => you must run the app in Electron environment (Nodejs environment)

  • require(‘electron’) => Webpack will bundle electron module to bundle.js => electron use fs module => Webpack doesn’t understand
  • window.require(‘electron’) => Webpack will ignore require function
  • In webpage environment, window.require will be undefined
  • In nodejs environment, window.require will work => Open your app in Electron window (not in browser window like Chrome, Firefox, etc.

I’m using typescript and I had to use

const electron = (<any>window).require("electron");

to get my renderer to communicate with my main. Hope this helps someone.

On windows with Vue CLI 3, window.require will work but will require a full path instead of a relative path from “node_modules/”.

But similar to the React case, webpack can be configured properly for Vue by editing vue.config.js. “require” will then work as expected.

vue.config.js:

module.exports = {
    configureWebpack: config => {
      if (process.env.NODE_ENV === 'production') {
        config.output.publicPath = `${process.cwd()}/dist/`
      }
      config.target = 'electron-renderer'
    }
  }

router.js (this is just an example, it would work in any other vue js file)

const Store = require("electron-store");
const store = new Store();

The only one good enough solution I found is following:

  1. install as dev dependency react-app-rewired package
  2. Use this module to inject custom Webpack config without eject-ing the CRA:
 "scripts": {
   ...
    "start": "react-app-rewired start",
  },
  1. and add the file config-overrides.js with according content:
module.exports = function override (config, env) {
  config.target = 'electron-renderer'
  return config;
}

Thanks to: @Lilanga comment, @agrublev comment and creators of react-app-rewired.

Updated: Actually the 2nd version of how to do the same: https://www.codementor.io/randyfindley/how-to-build-an-electron-app-using-create-react-app-and-electron-builder-ss1k0sfer

Updated2: @nukeop I removed some misleading statements due to lack of time to follow debates regarding why not to use Browserify or why to use it and can’t write down now detailed explanation what I meant. But I will keep personal opinion regarding that “just use window.require will solve the problem” seems very unreliable.

Using react-create-app I found the same errors. The solution so far was: const electron = window.require("electron") in the electron part BrowserWindow add nodeIntegration:true in the following way. mainWindow = new BrowserWindow({ width: 900, height: 680, webPreferences: { webSecurity: false, nodeIntegration: true } });

With Create-React-App 2, you can use the craco npm module:

https://www.npmjs.com/package/@craco/craco

Change react-scripts by craco in your package.json

craco.config.js

module.exports = {
    webpack: {
        configure: {
            target: 'electron-renderer'
        }
    }
};

I had the problems while require("fs") as well as window.require("fs"). Thanks to @Maxou44 for introduction CRACO in this discussion.

To solve the issue I made 3 changes in my project:

  1. Used CRACO as suggested by @Maxou44.
  2. In public/main.js(some might have named this file as electron.js), changed new BrowserWindow({ width: 1200, height: 800 }) to new BrowserWindow({ width: 1200, height: 800, webPreferences: { nodeIntegration: true } })
  3. Replaced const fs = require("fs") to const fs = require("electron").remote.require("fs")

Refer this git repo by @wwlib for more clarification https://github.com/wwlib/cra-craco-electron-example

Forgot to revert nodeIntegration: false which disables even window.require()... stupid mistake. Hope this spares someone an hour of researching.

make sure you’re compiling your code

I’m not using Electron, but Node-Webkit (nw.js). Using window.require did also fix my issue. Thanks very much for this!

For anyone still struggling with this especially regarding quitting the electron application through a react button please read on.

What helped for me was the following:

  1. When you declare your window make sure you set nodeIntegration: true as this is currently by default false for security reasons. Usually that is done in your electron.js file.

  2. As @packetstracer already mentioned: you have to relaunch the app in order to load Electron window with that config

mainWindow = new BrowserWindow({
//...
  webPreferences: {
    nodeIntegration: true,
  },
});
  1. Also in your electron.js put the following statement near the top this ensures that the ipcMain will listen on the event “close-me”:
const { ipcMain } = require("electron");
ipcMain.on("close-me", (evt, arg) => {
  app.quit();
});
  1. In the react component where your “close”-button is located add the following window.require statement after your imports. The usual require did not work:
const ipcRenderer = window.require("electron").ipcRenderer;
  1. And in order to close your application call the following statement. It should send the event “close-me” to the ipcMain:
<Button label="close" onClick={(e) => ipcRenderer.send("close-me")} />

Feel free to comment and give feedback I am still new to electron. I know the context with the quit button is unrelated but I could not find a better suited thread. Please feel free to point me to another more suitable thread if there is one.

My configuration:

"electron": "9.2.0",
"electron-builder": "22.8.0",
"electron-packager": "15.0.0",

@nukeop window.require did the trick for me as well thank you very much! 🎉

@nukeop - thanks for your last post; it helped me a lot. window.require worked for me.

Not even last comment helped me. Im trying to follow this article emphasis on simple in title. And then spend two days to resolve errors that should not be there. Its ether fs.existsSync is not a function or window.require is not a function

There are x solutions, and articles and all are different from another, including this topic. This is ridicules.

Today is 16.03.2012, allow me to ask: Does anyone has a suggestion how to modify simple script from article to work?

Hi, I am having the same error. When using window.require, the Uncaught TypeError: fs.readFileSync is not a function error is fixed but i came up with another error Uncaught TypeError: window.require is not a function.

Is there any suggestion on how to fix this. I am using browserify on node js.

window.require it’s not working for me when I call the function inside the React App. I got “TypeError: window.require is not a function”.

App.js:

import React, { useEffect, useState } from 'react';
import './App.css';

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

function App() {
    
    useEffect( () => {
        
        ipcRenderer.on('ping', (event, message) => { console.log(message) });
                
    }, []);
            
    return (
    <div className = 'App'>
        <div className = 'Header-Arrow'></div>
        <div className = 'Box'>
            <p>Press ⌘ + ⇧ + 4</p>
        </div>
    </div>
    );
}

export default App;

Mainwindow variable on Main.js:

// Create the browser window.
mainWindow = new BrowserWindow({
    alwaysOnTop: true,
    frame: false,
    fullscreenable: false,
    transparent: true,
    titleBarStyle: 'customButtonsOnHover',
    show: false,
    width: 300, 
    height: 350,
    webPreferences: {
        nodeIntegration: true,
        preload: __dirname + '/preload.js'
    }
});

Preload.js:

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

What I am missing?

@cperthuis, your fix worked for un-ejected CRA app using ‘react-app-rewired’. thank you. image

For anyone who happens to be struggling with this or similar issues and is using Vue and vue-electron-builder, see here

So, issue solved. I answer myself because maybe someone can benefit from it (I stucked for hours). If you have a Preload.js file, you don’t need to call window.require('electron').ipcRenderer again from the Àpp.js (renderer); you call directly the variable as window.ipcRenderer like this:

App.js:

import React, { useEffect, useState } from 'react';
import './App.css';

function App() {
    
    useEffect( () => {
        
        window.ipcRenderer.on('ping', (event, message) => { console.log(message) });
                
    }, []);
            
    return (
    <div className = 'App'>
        <div className = 'Header-Arrow'></div>
        <div className = 'Box'>
            <p>Press ⌘ + ⇧ + 4</p>
        </div>
    </div>
    );
}

export default App;

Mainwindow variable on Main.js:

// Create the browser window.
mainWindow = new BrowserWindow({
    alwaysOnTop: true,
    frame: false,
    fullscreenable: false,
    transparent: true,
    titleBarStyle: 'customButtonsOnHover',
    show: false,
    width: 300, 
    height: 350,
    webPreferences: {
        nodeIntegration: true,
        preload: __dirname + '/preload.js'
    }
});

Preload.js:

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

After running the app from the command line, the window generated by the React process will throw an error (ipcRenderer is undefined). Ignore it. The window generated by the Electron process (main app ) will work fine.

People who are still facing the issue, somehow doing a window.require() spoils the consistency of using import statements throughout the codebase. An easy alternative is to set your webpack target to electron-renderer. If using Create React App, ejecting can be a mess. Hence, you can use this package which does the webpack hooking for you and you can use for eg., import fs from 'fs' in your React components (or any other node module ), happily in your life 😃

If you get window.require is not a function and you are using Angular-CLI, use the following to instantiate the Window interface:

./src/typings.d.ts

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

Then you can use this to include electron: const { ipcRenderer } = window.require('electron');

@solominh Thanks for your explanations here. However I have a similar configuration as @holgersindbaek, which environment is a preload script in a webview?

Info:

  • My electron entry runs mainWindow.loadURL(url), where url is a local index.html
  • This index.html has a <webview>
  • The webview has a preload field which loads inject.js, and this script does the window.require(‘electron’).

Remarks:

  • If I use const electron = require('electron'), I have the error fs.readFileSync is not a function
  • If I use const electron = window.require('electron'), I have the error window.require is not a function.
  • inject.js is bundled through webpack (can paste config if relevant).

EDIT: Solved for me using <webview nodeintegration="true"> and window.require. Leaving note here for future reference.

@holgersindbaek You shouldn’t view your app in browser like Chrome, Firefox, etc. It won’t work because it is the webpage environment. You should view your app in an Electron window.

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?

I’m using electron-typescript-webpack-react and electron-forge.

This solution https://github.com/electron/electron/issues/9920#issuecomment-575839738 works only I put the window.api in index.html:

    window.api.receive("fromMain", (data) => {
        console.log(`Received ${data} from main process`);
    });
    window.api.send("toMain", "some data");

But if I put this code in the renderer process: renderer.ts I get the error:

Property 'api' does not exist on type 'Window & typeof globalThis'

I tried also to add

const ipcRenderer = window.require("electron").ipcRenderer; 

and I get

window.require is not a function

Thanks to @tumbledwyer for posting the link to a solution that works for me:

Updated: Actually the 2nd version of how to do the same: https://www.codementor.io/randyfindley/how-to-build-an-electron-app-using-create-react-app-and-electron-builder-ss1k0sfer

The solution from Randy Findley: Now, if you need to access the fs module like I did, you’ll quickly hit the Module not found error

First, install Rescripts.

yarn add @rescripts/cli @rescripts/rescript-env --dev

Then, change the scripts tags in package.json from this…

"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",

to this

"start": "rescripts start",
"build": "rescripts build",
"test": "rescripts test",

Now add a new file called .rescriptsrc.js with the following contents:

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

Finally add another new file called .webpack.config.js with the following contents:

// define child rescript
module.exports = config => {
  config.target = 'electron-renderer';
  return config;
}

Now you can use the fs module, no worries.

@cperthuis I just want to say THANK YOU!! Hahah I am not even using Vue, but logical thinking led me to figure out that i can change my webpack.config.js to have a target property 😃 image WORKED LIKE A CHARM.

This worked for me. For e.g. if you want to require remote, then

declare const window: any; const { remote } = window.require(‘electron’);

After many hours of trying to make this work, I finally got it to work using the contextBridge as other have suggested. I needed to access the electron shell object from my React Native Electron app code to open an external browser window as part of OAuth sign in flow. In electron.js:

const { app, BrowserWindow } = require('electron');
const path = require('path');
...
function createWindow() {
	const mainWindow = new BrowserWindow({
		width: 800,
		height: 600,
		webPreferences: {
			preload: path.join(app.getAppPath(), 'preload.js'), // make sure this path to you preload file is correct
		}
	});

in preload.js

const { contextBridge, shell } = require('electron')

contextBridge.exposeInMainWorld(
	'electron',
	{
		doThing: () => shell.openExternal("http://www.google.com")
	}
)

In the client application:

declare global {
	interface Window {
		// eslint-disable-next-line @typescript-eslint/no-explicit-any
		[x: string]: any;
		// eslint-disable-next-line @typescript-eslint/no-explicit-any
		require: any;
	}
}
...
const signInWithGoogle = (): void => {
	window.electron.doThing();
};

@timsamart it does works. Thank you, it saves me days of work. 🤣

I read whole thread from top to bottom and nothing worked. EXCEPT, @Saroopashree 's method above. Thanks @Saroopashree for sharing the solution. I guess since react and webpack have changed somewhat since the start of the thread, the solutions above are obsolete. I may be wrong ofcourse but using above method worked. This is what I did:

  1. Ran npm install craco -D
  2. Create config file craco.config.js and pasted following code module.exports = { webpack: { configure: { target: 'electron-renderer' } } };
  3. Updated new BrowserWindow({ width: 1200, height: 800, webPreferences: { nodeIntegration: true } }) in main.js. Ofcourse with different width and height.
  4. Ran npm start to start development server for react app created using create-react-app. This opened a tab with same errors. And I felt tired again.
  5. Ran npm run electron to run electron app. And Viola!!! I was able to see the page.

So thank you @Saroopashree.

I almost don’t want to add this but it would have saved me an hour or two of debugging.

I had to rm -rf node_modules && npm install to fix this issue. I was then able to remove the window from window.require and things started working again. Hopefully that helps somebody else.

Wow… Tried EVERYTHING in this thread and missed your replay because there no upvotes… Still got window is not a function… removed and reinstalled node_modules and its working.

PS: you still need to do all the solutions but if you did everything and still the same re install node_modules

You saved my day @joshuapinter !

@Maxou44 thanks for the tip about craco & electron. It is just what I needed. In case anyone is looking for an example…

https://github.com/wwlib/cra-craco-electron-example

I almost don’t want to add this but it would have saved me an hour or two of debugging.

I had to rm -rf node_modules && npm install to fix this issue. I was then able to remove the window from window.require and things started working again. Hopefully that helps somebody else.

For @ikiK-CRO and others, I created my own “starter kit” project with Electron/TypeScript/Svelte using the latest (as of this posting) versions of the libraries with contextIsolation=true. It may not be received well. I’m no expert at any of these, much less combining them. It uses the contextBridge to enforce isolation between the renderer process and the main process. The readme.md references other useful discussions on context isolation and the contextBridge, which is crucial to getting your goodies attached to the window object in your renderer process.

https://github.com/pglezen/electron-typescript-svelte-starter

Not even last comment helped me. Im trying to follow this article emphasis on simple in title.

Just so you know, that article references

 "electron": "^4.1.0",

in the package.json. The behavior of the nodeIntegration property is vastly different between that and Electron 12. If you’re really using Electron 4, then the article should work, since nodeIntegration: true was the default back then. But Electron 5 and later breaks this.

Thank you for the replay, I made rollback of everything to versions from article, still same thing:

electron-filetree-example@0.1.0 /Users/kiki/react/electron-filetree-example
└── electron@4.2.12 
electron-filetree-example@0.1.0 /Users/kiki/react/electron-filetree-example
└── react@16.14.0 

It is reporting window.require is not a functionon line var remote = window.require('electron').remote; in FileTree.js

and if I remove window I get fs.existsSync is not a function on node_modules/electron/index.js:8

Not even last comment helped me. Im trying to follow this article emphasis on simple in title.

Just so you know, that article references

 "electron": "^4.1.0",

in the package.json. The behavior of the nodeIntegration property is vastly different between that and Electron 12. If you’re really using Electron 4, then the article should work, since nodeIntegration: true was the default back then. But Electron 5 and later breaks this.

Why would window.require not be scalable?

If you are trying to access ‘electron’ or its other component inside a react component/class, then try this: #336757899

Ya, put declare const window: any; at the top, just after where the imports are.

window.require worked for me! Thanks guys!

Hi, thanks man.

On Sun, Dec 3, 2017 at 9:29 PM, Michael - ሚካኤል ሰልጠነ < notifications@github.com> wrote:

This worked for me. For e.g. if you want to require remote, then

declare const window: any; const { remote } = window.require(‘electron’);

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/electron/electron/issues/7300#issuecomment-348801405, or mute the thread https://github.com/notifications/unsubscribe-auth/ARDyd4c3aOkMbb058xklujMMbnmaoxKGks5s8uGVgaJpZM4KDU6t .

@nukeop The renderer process inside Electron has access to a full node/commonjs environment as well, so you don’t need to bundle anything.

If I do not pack the whole program into a bundle, I have no easy way of running it, as my main.js only starts electron and loads the html file that includes the bundle.

I’m not sure I understand here, any script you load in your HTML file has a full commonjs environment and can therefore use require to load in extra files without browserifying anything