How to pass parameters from main process to render processes in Electron
Asked Answered
T

9

35

I have an Electron app that can open different windows.

On app launch the app open a set of window(s) (that load the same HTML and JS files) but with params to change each window displayed infos.

Example :

app.on('ready', async () => {
  ...
  // open window for stuff 1
  win1 = new BrowserWindow({
     width: 1024,
     height: 728
  });
  win1.loadURL(`file://${__dirname}/app/app.html?id=1`);

  // open window for stuff 2
  win2 = new BrowserWindow({
     width: 1024,
     height: 728
  });
  win2.loadURL(`file://${__dirname}/app/app.html?id=2`);

Obviously passing params in file:// path doesn't work. I can't find a clear solution in Electron documentation or elsewhere on Internet to condition my rendered window to a param.

I can probably use IPC communication after window ready but it seems a little bit too complicated until I just want pass a variable to my child view.

P.S. : to be totally honest my application is built with React/Redux and the param I want to pass to view is the redux store key to listen for this view.

Thornton answered 12/7, 2016 at 17:1 Comment(6)
Passing params (and a hash) in the file:// path works just fine for me. All params are in location as expected.Pandolfi
I wouldn't use this parameterized URL concept for a file, if you would provide them on an HTTP Server ok. Instead you could do different things, e.g. use Cookies, read them on start. Or just use win1.webContents.executeJavaScript("var query = {id:1}")... at least it is a one linerBackler
I think both of your answer made the deal.Thornton
I don't see any problems with providing (short) parameters to a file://; That said I also use a preload script file from which I get a (possibly large) JSON page data string from the main thread and make it available to the page. This way the page data is available already during page load.Pandolfi
You seem to be asking the same question every 4 hours. youtube.com/watch?v=jKzBJAowmGgSuite
Electron's context bridge worked for me: https://mcmap.net/q/428665/-send-environment-variable-value-to-renderer-thread-in-electron-appThing
T
20

According atom source code the query string method is a reliable way to do that very simply, especially when we only need to pass a unique string param:

// main process
win1.loadURL(`file://${__dirname}/app/app.html?id=${id}`);

// rendered process
console.log(global.location.search);

https://github.com/electron/electron/issues/6504

Thornton answered 15/7, 2016 at 17:1 Comment(3)
better to be received as json object. Is there a way to achieve so?Nomo
@MagedSaeed yes, use global varialbe (check my answer below)Cuzco
The only problem with this otherwise elegant solution is that query string has a limited length so if you want to pass longer data like paragraphs from a PDF file, you need to resort to a different solution.Tael
Y
56

A few methods:

loadURL Query String

The query string method others have posted seems to work fine. It might even be the easiest.

additionalArguments

Electron's documentation says additionalArguments is:

Useful for passing small bits of data down to renderer process preload scripts.

Main

const win = new BrowserWindow({
  width: 800,
  height: 600,
  backgroundColor: '#000000'
  webPreferences: {
    additionalArguments: ["myvarvalue", "secondvarvalue", "--another=something"]
  }
});

Renderer

window.process.argv is going to look something like:

["--num-raster-threads=2",
"--enable-gpu-memory-buffer-compositor-resources",
"--enable-gpu-async-worker-context",
...
"--renderer-client-id=4",
"myvarvalue",
"secondvarvalue",
"--another=something"]

It will append an array of strings. You could do something window.process.argv.slice(-3) to grab the last items in the array.


IPC Main / Render

Like you said, it seems complicated for what you are trying to do, but maybe this helps:

Main

const { ipcMain } = require('electron');

var mainProcessVars = {
  somevar: "name",
  anothervar: 33
}

ipcMain.on('variable-request', function (event, arg) {
  event.sender.send('variable-reply', [mainProcessVars[arg[0]], mainProcessVars[arg[1]]]);
});

Renderer

const { ipcRenderer } = electron;

electron.ipcRenderer.send('variable-request', ['somevar', 'anothervar']);

ipcRenderer.on('variable-reply', function (event, args) {
  console.log(args[0]); // "name"
  console.log(args[1]); // 33
});

This way allows you to send data besides strings.

Yamamoto answered 25/1, 2019 at 6:5 Comment(4)
window.process is undefined in renderer nowadays unless webPreferences: { nodeIntegration: true, contextIsolation: false }. See #66612993Amphibology
@PsychoX I'ts true that process is undefined in renderer, but it can be accessed in preload script. You can then expose that (safely?) to renderer via contextBridge.Whitefish
additionalArguments work well, but I noticed that on Windows (or at least on my colleague's setup) there would be an additional argument (/prefetch:1) appended after them, so slice(-3) is not a reliable way of reading them.Mentally
additionalArguments is great. as @BartvanHeukelom suggested, I'd add a prefix to each string, for example "id=", and replace .slice() with let idArray = process.argv.filter((str) => str.startsWith('id='));Tychon
T
20

According atom source code the query string method is a reliable way to do that very simply, especially when we only need to pass a unique string param:

// main process
win1.loadURL(`file://${__dirname}/app/app.html?id=${id}`);

// rendered process
console.log(global.location.search);

https://github.com/electron/electron/issues/6504

Thornton answered 15/7, 2016 at 17:1 Comment(3)
better to be received as json object. Is there a way to achieve so?Nomo
@MagedSaeed yes, use global varialbe (check my answer below)Cuzco
The only problem with this otherwise elegant solution is that query string has a limited length so if you want to pass longer data like paragraphs from a PDF file, you need to resort to a different solution.Tael
G
17

Using query string along with win.loadFile(),

// main process or renderer process 1
data = {"age": 12, "healthy": true}

let win = new BrowserWindow({
        webPreferences: {
          nodeIntegration: true
        }
      });

win.loadFile("public/print.html", {query: {"data": JSON.stringify(data)}});
// renderer process 2
const querystring = require('querystring');
let query = querystring.parse(global.location.search);
let data = JSON.parse(query['?data'])
Garrison answered 15/12, 2019 at 18:52 Comment(4)
Is there a limit to how much data may be passed on the query string? Thanks!Lewison
@majorBummer I'm not sure. I never hit the limit.Garrison
I used this until it turned out to suffer from inappropriate caching. That is, the value of location.search (which in my case should be different every time I run the program) would be that of a previous invocation.Mentally
querystring is now deprecated. The querystring API is considered Legacy. While it is still maintained, new code should use the URLSearchParams API instead.Ber
T
3

We can inject Javascript code execution (on window did-finish-load event) and trigger react re-rendering with correct redux state portion. But it requires additional lifecycle step in rendered process.

Using hash or query in "local filepath" sounds a little bit weird but in my case it take sense since the hash describe the redux store branch to take in consideration in this window (file://which-code-to-load#which-content).

So even if i'm not totally at ease with this approach right now I'll take the second option.

It's a shame that the API doesn't provide any way to declare global vars from main process on window opening. I'll post a feature request.

Thornton answered 15/7, 2016 at 10:26 Comment(1)
can you elaborate / give examples for how these would work?Balmy
R
2

Actually use the invoke and handle combo can work as well. In my example, i especially need to establish a connection at the very beginning.

In preload

const { ipcRenderer } = require('electron')

window.addEventListener('DOMContentLoaded', () => {
  ipcRenderer.invoke('init').then(res => {
    window.alert(res)
    // render(e(App, {}), elem)
  })
})

And in main

const { ipcMain } = require('electron')

ipcMain.handle('init', async () => {
  return 'Abc'
})

You might even try to make a sync call via ipcRenderer.sendSync, maybe this is quicker, but I haven't tried that.

Rebato answered 31/10, 2021 at 23:38 Comment(0)
U
1

For example:

Process script

In your main script write:

global.varsForWindow = {
    platform: process.platform
};

Window script

Where you need it in a window script:

var varsFromMainScript = require('electron').remote.getGlobal('varsForWindow');
console.log(varsFromMainScript.platform);
Univocal answered 28/10, 2019 at 16:41 Comment(1)
Doesn't work if you need to pass different values to different windows...Balmy
C
1

You can share data between main and renderer processor using global variable:

Main processor:

global.id = 1;

Renderer processor:

let id = remote.getGlobal('id');
Cuzco answered 4/11, 2019 at 8:16 Comment(3)
I don't recommend this way. if developers want to create multiple windows consecutively.Garrison
the remote module has been deprecated and is no longer recommended for useLewison
besides, what is remote? another global?Rebato
C
0

If you're creating a new instance of BrowserWindow the query parameters don't seem to work at least in these versions:

"electron": "^26.2.2",
"nextron": "^8.12.0",

The workaround I found was to pass parameters through additionalArguments

      webPreferences: {
        additionalArguments: [
          `--metadata=${JSON.stringify(arg.data)}`,
        ],
      },

Then on your preload script

const metadata = process.argv.filter((arg) => arg.startsWith('--metadata='))[0]?.split('=')?.[1]
const data = JSON.parse(metadata || '{}')

contextBridge.exposeInMainWorld('appInfo', {
  metadata: data,
})

And then you can get it on your component with

global.appInfo.metadata
Crofton answered 20/11, 2023 at 16:59 Comment(0)
R
-5

Unfortunately, it seems pretty complicated. You probably won't be able to add variables to .html you render in electron in a nice way.

Of course - you can use url but it slows down booting very much (vy seconds) or you can execute Javascript in BrowserWindo, which also slows boot down.

The only way is the IPC and leaving javascript .html agnostic to variables from main process. Very sad though..

Recept answered 7/9, 2016 at 19:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.