Adding a Shutdown Dialog to Electron

We create open-source because we love it, and we share our finding so everyone else can benefit as well.

Adding a Shutdown Dialog to Electron

You may not realize it yet, but there are many occasions where a shutdown dialog can help with UX. For instance, alerting users to important information prior to the app closing. When using the Electron TrayMenu, you could be telling the user, “The app won’t close until you close it from the tray”. Maybe the user needs to manually save something, so you need to ask them, “Did you save your progress?”. Or maybe you simply want to give the users statistics about their session. Regardless of the use-case, we’ll look at how to implement this while making sure it works on all platforms.

If you want to follow along and test the code, I will demonstrate using the Electron API Demos App.

Creating a Shutdown Dialog in the Close Event

We want to show a dialog before closing, but also offer the option to interrupt the close. Electron’s BrowserWindow ‘close’ event is perfect, as it is triggered anytime a user closes the Main Window. It is triggered with a hotkey, close button, and even the menu Exit/Quit menu item.

We listen to the event from our initiated BrowserWindow. For now, we’ll add the logic needed to shutdown the app when the window is closed:

// main.js file

mainWindow.on('close', e => {

Bonus: If you are using the TrayMenu, you can use this same block of code to close the app completely when the window is closed.

Using ShowMessageBox as a Shutdown Dialog

We still need to add the dialog that pops up during our close event, so let’s put that into place. After we make sure to add the dialog to the Electron import, we will add our dialog box:

const {app, dialog, BrowserWindow} = require('electron') // Line 7 of main.js


mainWindow.on('close', e => { // Line 49
    type: 'info',
    buttons: ['Ok', 'Exit'],
    cancelId: 1,
    defaultId: 0,
    title: 'Warning',
    detail: 'Hey, wait! There\'s something you should know...'
  }).then(({ response, checkboxChecked }) => {
    console.log(`response: ${response}`)
    if (response) {

When added, start the app, and close the window. As long as you don’t do a Ctrl+C from the CLI, you will get a dialog like this:

Electron shutdown dialog

Once you see the dialog, you can use Ok to stay in the app, or use Exit to close the app. The Ok option can of course be clicked, or you can simply press Enter. Same with Exit, you can be click it or execute with the Esc hotkey. It should also be noted that the response from Ok is 0, and a 1 from Exit. With that in mind, look at the options, and we’ll see these responses are literally the element indices for the buttons array. When the response is 1, we make sure to shutdown the app completely.

Working on All Platforms

One of the most important lines of this dialog setup is the preventDefault event handler. When we’re closing the window, the default response is to obviously close the window. If you place this after the dialog, you’ll find the dialog works in OSX, but Windows and Linux will see a dialog flash as the app closes. To prevent this, we place this before the dialog, and everything works as intended. This may seem like common sense, and it is, but there are some examples in the wild which have this alternative placement.

Either way, that’s everything you need to set this shutdown dialogs.

MessageBox Nuances

There are a few nuances with the MessageBox, but let me preface this by saying, you can create a default dialog without any options. Neat! Of course that means the MessageBox must have some automatic or default options. As you surely noticed, the cancelId option sets the MessageBox’s Cancel function to Exit, and likewise the defaultId chooses the highlighted button. You will almost never need both of these options, and we can actually remove the cancelId option to get the same results.

Automated cancelIds

Still I wanted to point out a couple special cases with the MessageBox: When the defaultId is set to 0, cancelId is not set, and there is more than one button in the buttons array, MessageBox will automatically set the cancelId to 1. If defaultId isn’t set, cancelId is set to 0. This can cause some confusion in certain cases, so keep that in mind with this next section.

Note: When the defaultId and cancelIds are both the same. the highlight is switched to a gray disabled background color.

Reserved Button Names

Maybe with the context of the dialog message, you want users to “cancel closing the window”. Unfortunately, when you set the name of the button to Cancel or No, MessageBox may make some changes automatically. When the cancelId is not set, it will be automatically set to the ID of this button. If you have both a Cancel and No button, it will be set to the first in the array. Your may also notice the button order change. Cancel and No are automatically placed in the opposing positions. To see this in action, replace our example’s Exit button with Cancel:

Dialog Cancel Button Placement
The Cancel button (Element 1) is now on the left because of its name.

The Source of this Logic

Most of these nuances require the cancelId to be null, but in earlier versions of Electron it happened even with the cancelId. To better understand the logic, here is the relevant portion of the dialog api source:

  // Choose a default button to get selected when dialog is cancelled.
  if (cancelId == null) {
    // If the defaultId is set to 0, ensure the cancel button is a different index (1)
    cancelId = (defaultId === 0 && buttons.length > 1) ? 1 : 0;
    for (let i = 0; i < buttons.length; i++) {
      const text = buttons[i].toLowerCase();
      if (text === 'cancel' || text === 'no') {
        cancelId = i;

If you liked this article or have questions, let me know in the comments! Also, if you are looking for more about Electron, check out my Electron production checklist in Writing Complete Apps in Electron. Happy coding!


No Comments

Add your comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.