Multiple Window Electron App

Jason Sturges
3 min readNov 30, 2020
Photo by Daniel von Appen on Unsplash

Electron apps seem classically one window, with one instance of a web application running. This is frustrating, even if you just want multiple instances of the same app running.

In my project, I wanted to easily open multiple instances similar to Visual Studio Code’s File ≫ New Window.

This example is based on Electron React Boilerplate as the initial project scaffolding.

Under app/main.dev.js there’s logic for just one mainWindow — first expand that to be a JavaScript Set of windows. Replace mainWindow instance of BrowserWindow with a new reference to hold all windows

const windows = new Set();

In the createWindow() method, each time a new window is created:

  • Offset new widow’s position slightly (24 pixels)
  • When the window is closed, remove it from the windows set
  • When the window is focused, update the menu
export const createWindow = async () => {
if (
process.env.NODE_ENV === 'development' ||
process.env.DEBUG_PROD === 'true'
) {
await installExtensions();
}
let x, y; const currentWindow = BrowserWindow.getFocusedWindow(); if (currentWindow) {
const [currentWindowX, currentWindowY] = currentWindow.getPosition();
x = currentWindowX + 24;
y = currentWindowY + 24;
}
let newWindow = new BrowserWindow({
show: false,
width: 1200,
height: 812,
x,
y,
webPreferences: {
nodeIntegration: true
}
});
newWindow.loadURL(`file://${__dirname}/app.html`); newWindow.webContents.on('did-finish-load', () => {
if (!newWindow) {
throw new Error('"newWindow" is not defined');
}
if (process.env.START_MINIMIZED) {
newWindow.minimize();
} else {
newWindow.show();
newWindow.focus();
}
});
newWindow.on('closed', () => {
windows.delete(newWindow);
newWindow = null;
});
newWindow.on('focus', () => {
const menuBuilder = new MenuBuilder(newWindow);
menuBuilder.buildMenu();
});
windows.add(newWindow);
return newWindow;
};

Finally, update the app’s activate event handler for macOS:

app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (windows.size === 0) createWindow();
});

Of course, you’ll need some way to trigger launching the new window, such as inter process communication or simply adding to the app’s menu.

Menu

Open the new window by creating a File menu group, with accelerator keyboard shortcut keys.

Import the createWindow function in menu.js

import { createWindow } from './main.dev';

Create the Darwin menu implementation:

const subMenuFile = {
label: 'File',
submenu: [
{
label: 'New Window',
accelerator: 'Shift+Command+N',
click: () => {
createWindow();
}
}
]
};

Remember to add the new subMenuFile menu group to the Darwin template

return [
subMenuAbout,
subMenuFile,
subMenuEdit,
subMenuView,
subMenuWindow,
subMenuHelp
];

…and corresponding default template implementation:

{
label: '&File',
submenu: [
{
label: 'New',
accelerator: 'Ctrl+N',
click: () => {
this.mainWindow.send('file-new');
}
},
{
label: 'New Window',
accelerator: 'Shift+Ctrl+N',
click: () => {
createWindow();
}
},
{ type: 'separator' },
...

Now you can easily launch multiple windows of your application.

Continue to part two, where I’ll demonstrate sending messages between windows using inter process communication for sharing data such as complex clipboard copy and paste:

--

--

Jason Sturges

Avant-garde experimental artist — creative professional leveraging technology for immersive experiences