How to Handle Multiple Windows in an Electron App

Electron is a framework that allows developers to create desktop applications using Node.js. In this framework, code gets separated between the main process and one or many renderer processes. In Electron, each window corresponds to a renderer process. Here’s a way of setting up the logic of handling multiple windows in the main process of your Electron app.

Setting Up Multiple Windows in the Main Process

The code for a basic main process that supports a single window might look something like this:

let mainWindow;

const createWindow = () => {
    mainWindow = new BrowserWindow()
    mainWindow.loadURL(`file://${__dirname}/app/index.html`);
}

app.on("ready", createWindow);
app.on("activate", createWindow);

At the top of the code, we have a mainWindow variable. Let’s change the line a bit to support storing more than one window at a time.

let windows = new Set();

We want to use a set instead of an array to store the windows for reasons that will become clear in a moment.

Now, let’s look back at the createWindow function.

const createWindow = () => {
    let window = new BrowserWindow()
    windows.add(window)
    window.loadURL(`file://${__dirname}/app/index.html`);
}

Not much has changed here. All we needed to do was add the newly-created window to our set of windows.

At this point, the main process should be all set up to handle more than one window. You are probably going to want to add something to your renderer process that will create a window so users can actually create them. Assuming we have that set up to send an event named something like “add-window” to our main process, we’ll then add some code that looks like this:

app.on("add-window", createWindow);

Logic for Closing Multiple Windows

Great! We have just have one more thing to add to our createWindow function: logic to properly close windows. After adding that, our createWindow function will look like this:

const createWindow = () => {
    let window = new BrowserWindow()
    windows.add(window)
    window.on("closed", () => {
        windows.delete(window);
        window = null;
    });
    window.loadURL(`file://${__dirname}/app/index.html`);
}

Here we can see why we’re using a set to store our windows instead of an array. In sets, an element’s key is equal to its value. This means that when we need to remove a window from our set, we don’t need to go through the trouble of finding exactly which window in the array is the one we need to remove (this is actually pretty complicated in Electron).

The Finishing Touch

There’s one last finishing touch we need to add. We currently have this line in our code:

app.on("activate", createWindow);

This line is specifically for OS X. Because most desktop apps on OS X don’t automatically exit when all windows have closed, we need to check for the scenario where the desktop icon gets clicked on and all windows are closed but the app is still running. This is what triggers the “activate” event.

This works great when we only have to worry about one window but not anymore now that we are allowing multiple windows in our Electron app. Now, whenever a user clicks the desktop icon, we will create a new window even when some windows are still open. Luckily, fixing this problem is as simple as this:

app.on("activate", () => {
    if (windows.size === 0) {
        createWindow();
    }
});

All we’re doing is checking to make sure we don’t have any windows open before creating a new one.

Handling Multiple Windows in an Electron App

With this done, our app can now handle multiple windows. You can also further build onto this to support more advanced logic. Some examples might be automatic split-screen when a new window is opened, setting a maximum number of windows, etc.