Logo Vincent
Back to all posts

Electron in Practice: Customizing Mac Menus

Electron
Electron in Practice: Customizing Mac Menus

Preface

Previous articles covered developing an Electron app through to Mac packaging.

Related articles:

Learn Electron in One Article

Electron in Practice: Local Database SQLite

Electron in Practice: Managing Electron Projects with Monorepo

Electron in Practice: Mac Packaging

Electron in Practice: Registering an Apple Developer Account

Electron in Practice: Mac App Signing and Notarization

Electron in Practice: Customizing DMG Installers

This article covers customizing Mac menus.

Default Mac Menu

When developing a Mac app with Electron,

if you don’t customize the Mac menu,

the default looks like this:

You can customize the Mac menu bar using Electron’s Menu API.

https://www.electronjs.org/zh/docs/latest/api/menu

The core code is as follows, where template is a menu template array:

const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);

Using Built-in Roles

A common approach in menu templates is to use built-in roles.

Electron wraps common menu items and their click actions into roles:

[
  {
    label: 'Edit',
    submenu: [
      { role: 'undo' },
      { role: 'redo' },
      { type: 'separator' },
      { role: 'cut' },
      { role: 'copy' },
      { role: 'paste' },
    ],
  },
];

For example, the copy role above will perform a copy operation when clicked.

Using Custom Menu Items

In addition to built-in roles, you can define custom menu items:

[
  {
    role: 'help',
    submenu: [
      {
        label: 'Learn More',
        click: async () => {
          const { shell } = require('electron');
          await shell.openExternal('https://electronjs.org');
        },
      },
    ],
  },
];

Common Template

Here is a common template based on typical Chinese app menus:

[
  {
    label: 'app',
    submenu: [
      {
        label: 'About',
        role: 'about',
      },
      {
        type: 'separator',
      },
      {
        label: 'Hide',
        role: 'hide',
      },
      {
        label: 'Hide Others',
        role: 'hideOthers',
      },
      {
        type: 'separator',
      },
      {
        label: 'Quit',
        role: 'quit',
      },
    ],
  },
  {
    label: 'Edit',
    submenu: [
      {
        label: 'Undo',
        role: 'undo',
      },
      {
        label: 'Redo',
        role: 'redo',
      },
      {
        type: 'separator',
      },
      {
        label: 'Cut',
        role: 'cut',
      },
      {
        label: 'Copy',
        role: 'copy',
      },
      {
        label: 'Paste',
        role: 'paste',
      },
      {
        label: 'Delete',
        role: 'delete',
      },
      {
        label: 'Select All',
        role: 'selectAll',
      },
    ],
  },
  {
    label: 'Window',
    submenu: [
      {
        label: 'Minimize',
        role: 'minimize',
      },
      {
        label: 'Close',
        role: 'close',
      },
      {
        label: 'Toggle Fullscreen',
        role: 'togglefullscreen',
      },
    ],
  },
  {
    label: 'Debug',
    submenu: [
      {
        label: 'DevTools',
        role: 'toggleDevTools',
      },
    ],
  },
];

Result

qiao-x-menu

A library for working with Electron menus has been created — feel free to use it:

https://code.insistime.com/#/qiao-x-menu

The menu customization above can be simplified to:

import { setApplicationMenu } from 'qiao-x-menu';

setApplicationMenu(menus);
© 2026 Vincent. All rights reserved.