docs: add Menu module tutorials (#47268)

* docs: add `Menu` module tutorials

* link API docs to new tutorials

* removed unreferenced fiddles

* add wording for new types

* fix import sort errors

* delete accelerator.md

* fixes
This commit is contained in:
Erick Zhao 2025-07-15 15:09:32 -07:00 committed by GitHub
commit cc9ca4bee2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
39 changed files with 1240 additions and 1472 deletions

View file

@ -0,0 +1,208 @@
---
title: Application Menu
description: Customize the main application menu for your Electron app
slug: application-menu
hide_title: true
---
# Application Menu
Each Electron app has a single top-level application menu.
* On macOS, this menu is shown in the system [menu bar](https://support.apple.com/en-ca/guide/mac-help/mchlp1446/mac).
* On Windows and Linux, this menu is shown at the top of each [BaseWindow](../api/base-window.md).
## Building application menus
The application menu can be set by passing a [Menu](../api/menu.md) instance into the
[`Menu.setApplicationMenu`](../api/menu.md#menusetapplicationmenumenu) static function.
When building an application menu in Electron, each top-level array menu item **must be a submenu**.
Electron will set a default menu for your app if this API is never called. Below is an example of
that default menu being created manually using shorthand [`MenuItem` roles](./menus.md#roles).
```js title='Manually creating the default menu' @ts-expect-error=[107]
const { shell } = require('electron/common')
const { app, Menu } = require('electron/main')
const isMac = process.platform === 'darwin'
const template = [
// { role: 'appMenu' }
...(isMac
? [{
label: app.name,
submenu: [
{ role: 'about' },
{ type: 'separator' },
{ role: 'services' },
{ type: 'separator' },
{ role: 'hide' },
{ role: 'hideOthers' },
{ role: 'unhide' },
{ type: 'separator' },
{ role: 'quit' }
]
}]
: []),
// { role: 'fileMenu' }
{
label: 'File',
submenu: [
isMac ? { role: 'close' } : { role: 'quit' }
]
},
// { role: 'editMenu' }
{
label: 'Edit',
submenu: [
{ role: 'undo' },
{ role: 'redo' },
{ type: 'separator' },
{ role: 'cut' },
{ role: 'copy' },
{ role: 'paste' },
...(isMac
? [
{ role: 'pasteAndMatchStyle' },
{ role: 'delete' },
{ role: 'selectAll' },
{ type: 'separator' },
{
label: 'Speech',
submenu: [
{ role: 'startSpeaking' },
{ role: 'stopSpeaking' }
]
}
]
: [
{ role: 'delete' },
{ type: 'separator' },
{ role: 'selectAll' }
])
]
},
// { role: 'viewMenu' }
{
label: 'View',
submenu: [
{ role: 'reload' },
{ role: 'forceReload' },
{ role: 'toggleDevTools' },
{ type: 'separator' },
{ role: 'resetZoom' },
{ role: 'zoomIn' },
{ role: 'zoomOut' },
{ type: 'separator' },
{ role: 'togglefullscreen' }
]
},
// { role: 'windowMenu' }
{
label: 'Window',
submenu: [
{ role: 'minimize' },
{ role: 'zoom' },
...(isMac
? [
{ type: 'separator' },
{ role: 'front' },
{ type: 'separator' },
{ role: 'window' }
]
: [
{ role: 'close' }
])
]
},
{
role: 'help',
submenu: [
{
label: 'Learn More',
click: async () => {
const { shell } = require('electron')
await shell.openExternal('https://electronjs.org')
}
}
]
}
]
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
```
> [!IMPORTANT]
> On macOS, the first submenu of the application menu will **always** have your application's name
> as its label. In general, you can populate this submenu by conditionally adding a menu item with
> the `appMenu` role.
> [!TIP]
> For additional descriptions of available roles, see the [`MenuItem` roles](./menus.md#roles)
> section of the general Menus guide.
### Using standard OS menu roles
Defining each submenu explicitly can get very verbose. If you want to re-use default submenus
in your app, you can use various submenu-related roles provided by Electron.
```js title='Using default roles for each submenu' @ts-expect-error=[26]
const { shell } = require('electron/common')
const { app, Menu } = require('electron/main')
const template = [
...(process.platform === 'darwin'
? [{ role: 'appMenu' }]
: []),
{ role: 'fileMenu' },
{ role: 'editMenu' },
{ role: 'viewMenu' },
{ role: 'windowMenu' },
{
role: 'help',
submenu: [
{
label: 'Learn More',
click: async () => {
const { shell } = require('electron')
await shell.openExternal('https://electronjs.org')
}
}
]
}
]
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
```
> [!NOTE]
> On macOS, the `help` role defines a top-level Help submenu that has a search bar for
> other menu items. It requires items to be added to its `submenu` to function.
## Setting window-specific application menus _Linux_ _Windows_
Since the root application menu exists on each `BaseWindow` on Windows and Linux, you can override
it with a window-specific `Menu` instance via the [`win.setMenu`](../api/browser-window.md#winsetmenumenu-linux-windows) method.
```js title='Override a window's menu'
const { BrowserWindow, Menu } = require('electron/main')
const win = new BrowserWindow()
const menu = Menu.buildFromTemplate([
{
label: 'my custom menu',
submenu: [
{ role: 'copy' },
{ role: 'paste' }
]
}
])
win.setMenu(menu)
```
> [!TIP]
> You can remove a specific window's application menu by calling the
> [`win.removeMenu`](../api/base-window.md#winremovemenu-linux-windows) API.

View file

@ -0,0 +1,69 @@
---
title: Context Menu
description: Configure cross-platform native OS menus with the Menu API.
slug: context-menu
hide_title: true
---
# Context Menu
Context menus are pop-up menus that appear when right-clicking (or pressing a shortcut
such as <kbd>Shift</kbd> + <kbd>F10</kbd> on Windows) somewhere in an app's interface.
No context menu will appear by default in Electron. However, context menus can be created by using
the [`menu.popup`](../api/menu.md#menupopupoptions) function on an instance of the
[Menu](../api/menu.md) class. You will need to listen for specific context menu events and set up
the trigger for `menu.popup` manually.
There are two ways of listening for context menu events in Electron: either via the main process
through [webContents](../api/web-contents.md) or in the renderer process via the
[`contextmenu`](https://developer.mozilla.org/en-US/docs/Web/API/Element/contextmenu_event) web event.
## Using the `context-menu` event (main)
Whenever a right-click is detected within the bounds of a specific `WebContents` instance, a
[`context-menu`](../api/web-contents.md#event-context-menu) event is triggered. The `params` object
passed to the listener provides an extensive list of attributes to distinguish which type of element
is receiving the event.
For example, if you want to provide a context menu for links, check for the `linkURL` parameter.
If you want to check for editable elements such as `<textarea/>`, check for the `isEditable` parameter.
```fiddle docs/fiddles/menus/context-menu/web-contents
```
## Using the `contextmenu` event (renderer)
Alternatively, you can also listen to the [`contextmenu`](https://developer.mozilla.org/en-US/docs/Web/API/Element/contextmenu_event)
event available on DOM elements in the renderer process and call the `menu.popup` function via IPC.
> [!TIP]
> To learn more about IPC basics in Electron, see the [Inter-Process Communication](./ipc.md) guide.
```fiddle docs/fiddles/menus/context-menu/dom|focus=preload.js
```
## Additional macOS menu items (e.g. Writing Tools)
On macOS, the [Writing Tools](https://support.apple.com/en-ca/guide/mac-help/mchldcd6c260/15.0/mac/15.0),
[AutoFill](https://support.apple.com/en-mz/guide/safari/ibrwf71ba236/mac), and
[Services](https://support.apple.com/en-ca/guide/mac-help/mchlp1012/mac) menu items
are disabled by default for context menus in Electron. To enable these features, pass the
[WebFrameMain](../api/web-frame-main.md) associated to the target `webContents` to the `frame`
parameter in `menu.popup`.
```js title='Associating a frame to the context menu'
const { BrowserWindow, Menu } = require('electron/main')
const menu = Menu.buildFromTemplate([{ role: 'editMenu' }])
const win = new BrowserWindow()
win.webContents.on('context-menu', (_event, params) => {
// Whether the context is editable.
if (params.isEditable) {
menu.popup({
// highlight-next-line
frame: params.frame
})
}
})
```

View file

@ -1,127 +1,170 @@
---
title: "Keyboard Shortcuts"
description: "Define accelerator strings for local and global keyboard shortcuts"
slug: keyboard-shortcuts
hide_title: false
---
# Keyboard Shortcuts
## Overview
## Accelerators
This feature allows you to configure local and global keyboard shortcuts
for your Electron application.
Accelerators are strings that can be used to represent keyboard shortcuts throughout your Electron.
These strings can contain multiple modifiers keys and a single key code joined by the `+` character.
## Example
> [!NOTE]
> Accelerators are **case-insensitive**.
### Local Shortcuts
### Available modifiers
Local keyboard shortcuts are triggered only when the application is focused.
To configure a local keyboard shortcut, you need to specify an [`accelerator`][]
property when creating a [MenuItem][] within the [Menu][] module.
* `Command` (or `Cmd` for short)
* `Control` (or `Ctrl` for short)
* `CommandOrControl` (or `CmdOrCtrl` for short)
* `Alt`
* `Option`
* `AltGr`
* `Shift`
* `Super` (or `Meta` as alias)
Starting with a working application from the
[tutorial starter code][tutorial-starter-code], update the `main.js` to be:
### Available key codes
```fiddle docs/fiddles/features/keyboard-shortcuts/local
const { app, BrowserWindow, Menu, MenuItem } = require('electron/main')
* `0` to `9`
* `A` to `Z`
* `F1` to `F24`
* Various Punctuation: `)`, `!`, `@`, `#`, `$`, `%`, `^`, `&`, `*`, `(`, `:`, `;`, `:`, `+`, `=`, `<`, `,`, `_`, `-`, `>`, `.`, `?`, `/`, `~`, `` ` ``, `{`, `]`, `[`, `|`, `\`, `}`, `"`
* `Plus`
* `Space`
* `Tab`
* `Capslock`
* `Numlock`
* `Scrolllock`
* `Backspace`
* `Delete`
* `Insert`
* `Return` (or `Enter` as alias)
* `Up`, `Down`, `Left` and `Right`
* `Home` and `End`
* `PageUp` and `PageDown`
* `Escape` (or `Esc` for short)
* `VolumeUp`, `VolumeDown` and `VolumeMute`
* `MediaNextTrack`, `MediaPreviousTrack`, `MediaStop` and `MediaPlayPause`
* `PrintScreen`
* NumPad Keys
* `num0` - `num9`
* `numdec` - decimal key
* `numadd` - numpad `+` key
* `numsub` - numpad `-` key
* `nummult` - numpad `*` key
* `numdiv` - numpad `÷` key
function createWindow () {
const win = new BrowserWindow({
width: 800,
height: 600
})
### Cross-platform modifiers
win.loadFile('index.html')
}
Many modifier accelerators map to different keys between operating systems.
| Modifier | macOS | Windows and Linux |
|------------------|-------------|----------------------|
|`CommandOrControl`| Command (⌘) | Control |
|`Command` | Command (⌘) | N/A |
|`Control` | Control (^) | Control |
|`Alt` | Option (⌥) | Alt |
|`Option` | Option (⌥) | N/A |
|`Super` (`Meta`) | Command (⌘) | Windows (⊞) |
> [!IMPORTANT]
>
> * On Linux and Windows, the `Command` modifier does not have any effect. In general, you should use
> the `CommandOrControl` modifier instead, which represents <kbd>⌘ Cmd</kbd> on macOS and <kbd>Ctrl</kbd>
> on Linux and Windows.
> * Use `Alt` instead of `Option`. The <kbd>⌥ Opt</kbd> key only exists on macOS, whereas the `Alt` will
> map to the appropriate modifier on all platforms.
#### Examples
Here are some examples of cross-platform Electron accelerators for common editing operations:
* Copy: `CommandOrControl+C`
* Paste: `CommandOrControl+V`
* Undo: `CommandOrControl+Z`
* Redo: `CommandOrControl+Shift+Z`
## Local shortcuts
**Local** keyboard shortcuts are triggered only when the application is focused. These shortcuts
map to specific menu items within the app's main [application menu](./application-menu.md).
To define a local keyboard shortcut, you need to configure the `accelerator` property when creating
a [MenuItem](../api/menu-item.md). Then, the `click` event associated to that menu item will trigger
upon using that accelerator.
```js title='Opening a dialog via accelerator (local)'
const { dialog, Menu, MenuItem } = require('electron/main')
const menu = new Menu()
menu.append(new MenuItem({
label: 'Electron',
submenu: [{
role: 'help',
accelerator: process.platform === 'darwin' ? 'Alt+Cmd+I' : 'Alt+Shift+I',
click: () => { console.log('Electron rocks!') }
}]
}))
Menu.setApplicationMenu(menu)
app.whenReady().then(createWindow)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
```
> NOTE: In the code above, you can see that the accelerator differs based on the
user's operating system. For MacOS, it is `Alt+Cmd+I`, whereas for Linux and
Windows, it is `Alt+Shift+I`.
After launching the Electron application, you should see the application menu
along with the local shortcut you just defined:
![Menu with a local shortcut](../images/local-shortcut.png)
If you click `Help` or press the defined accelerator and then open the terminal
that you ran your Electron application from, you will see the message that was
generated after triggering the `click` event: "Electron rocks!".
### Global Shortcuts
To configure a global keyboard shortcut, you need to use the [globalShortcut][]
module to detect keyboard events even when the application does not have
keyboard focus.
Starting with a working application from the
[tutorial starter code][tutorial-starter-code], update the `main.js` to be:
```fiddle docs/fiddles/features/keyboard-shortcuts/global
const { app, BrowserWindow, globalShortcut } = require('electron/main')
function createWindow () {
const win = new BrowserWindow({
width: 800,
height: 600
})
win.loadFile('index.html')
// The first submenu needs to be the app menu on macOS
if (process.platform === 'darwin') {
const appMenu = new MenuItem({ role: 'appMenu' })
menu.append(appMenu)
}
app.whenReady().then(() => {
globalShortcut.register('Alt+CommandOrControl+I', () => {
console.log('Electron loves global shortcuts!')
})
}).then(createWindow)
// highlight-start
const submenu = Menu.buildFromTemplate([{
label: 'Open a Dialog',
click: () => dialog.showMessageBox({ message: 'Hello World!' }),
accelerator: 'CommandOrControl+Alt+R'
}])
menu.append(new MenuItem({ label: 'Custom Menu', submenu }))
// highlight-end
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
Menu.setApplicationMenu(menu)
```
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
In the above example, a native "Hello World" dialog will open when pressing <kbd>⌘ Cmd</kbd>+<kbd>⌥ Opt</kbd>+<kbd>R</kbd>
on macOS or <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>R</kbd> on other platforms.
> [!TIP]
> Accelerators can work even when menu items are hidden. On macOS, this feature can be disabled by
> setting `acceleratorWorksWhenHidden: false` when building a `MenuItem`.
> [!TIP]
> On Windows and Linux, the `registerAccelerator` property of the `MenuItem` can be set to `false`
> so that the accelerator is visible in the system menu but not enabled.
## Global shortcuts
**Global** keyboard shortcuts work even when your app is out of focus. To configure a global keyboard
shortcut, you can use the [`globalShortcut.register`](../api/global-shortcut.md#globalshortcutregisteraccelerator-callback)
function to specify shortcuts.
```js title='Opening a dialog via accelerator (global)'
const { dialog, globalShortcut } = require('electron/main')
globalShortcut.register('CommandOrControl+Alt+R', () => {
dialog.showMessageBox({ message: 'Hello World!' })
})
```
> NOTE: In the code above, the `CommandOrControl` combination uses `Command`
on macOS and `Control` on Windows/Linux.
To later unregister a shortcut, you can use the [`globalShortcut.unregisterAccelerator`](../api/global-shortcut.md#globalshortcutunregisteraccelerator)
function.
After launching the Electron application, if you press the defined key
combination then open the terminal that you ran your Electron application from,
you will see that Electron loves global shortcuts!
```js title='Opening a dialog via accelerator (global)'
const { globalShortcut } = require('electron/main')
### Shortcuts within a BrowserWindow
globalShortcut.unregister('CommandOrControl+Alt+R')
```
#### Using web APIs
> [!WARNING]
> On macOS, there's a long-standing bug with `globalShortcut` that prevents it from working with
> keyboard layouts other than QWERTY ([electron/electron#19747](https://github.com/electron/electron/issues/19747)).
If you want to handle keyboard shortcuts within a [BrowserWindow][], you can
listen for the `keyup` and `keydown` [DOM events][dom-events] inside the
renderer process using the [addEventListener() API][addEventListener-api].
## Shortcuts within a window
### In the renderer process
If you want to handle keyboard shortcuts within a [BaseWindow](../api/base-window.md), you can
listen for the [`keyup`](https://developer.mozilla.org/en-US/docs/Web/API/Element/keyup_event) and
[`keydown`](https://developer.mozilla.org/en-US/docs/Web/API/Element/keydown_event) DOM Events inside
the renderer process using the [addEventListener](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener) API.
```fiddle docs/fiddles/features/keyboard-shortcuts/web-apis|focus=renderer.js
function handleKeyPress (event) {
@ -141,18 +184,14 @@ window.addEventListener('keyup', handleKeyPress, true)
#### Intercepting events in the main process
The [`before-input-event`](../api/web-contents.md#event-before-input-event) event
is emitted before dispatching `keydown` and `keyup` events in the page. It can
is emitted before dispatching `keydown` and `keyup` events in the renderer process. It can
be used to catch and handle custom shortcuts that are not visible in the menu.
Starting with a working application from the
[tutorial starter code][tutorial-starter-code], update the `main.js` file with the
following lines:
```fiddle docs/fiddles/features/keyboard-shortcuts/interception-from-main
```js title='Intercepting the Ctrl+I event from the main process'
const { app, BrowserWindow } = require('electron/main')
app.whenReady().then(() => {
const win = new BrowserWindow({ width: 800, height: 600 })
const win = new BrowserWindow()
win.loadFile('index.html')
win.webContents.on('before-input-event', (event, input) => {
@ -163,49 +202,3 @@ app.whenReady().then(() => {
})
})
```
After launching the Electron application, if you open the terminal that you ran
your Electron application from and press `Ctrl+I` key combination, you will
see that this key combination was successfully intercepted.
#### Using third-party libraries
If you don't want to do manual shortcut parsing, there are libraries that do
advanced key detection, such as [mousetrap][]. Below are examples of usage of the
`mousetrap` running in the Renderer process:
```js @ts-nocheck
Mousetrap.bind('4', () => { console.log('4') })
Mousetrap.bind('?', () => { console.log('show shortcuts!') })
Mousetrap.bind('esc', () => { console.log('escape') }, 'keyup')
// combinations
Mousetrap.bind('command+shift+k', () => { console.log('command shift k') })
// map multiple combinations to the same callback
Mousetrap.bind(['command+k', 'ctrl+k'], () => {
console.log('command k or control k')
// return false to prevent default behavior and stop event from bubbling
return false
})
// gmail style sequences
Mousetrap.bind('g i', () => { console.log('go to inbox') })
Mousetrap.bind('* a', () => { console.log('select all') })
// konami code!
Mousetrap.bind('up up down down left right left right b a enter', () => {
console.log('konami code')
})
```
[Menu]: ../api/menu.md
[MenuItem]: ../api/menu-item.md
[globalShortcut]: ../api/global-shortcut.md
[`accelerator`]: ../api/accelerator.md
[BrowserWindow]: ../api/browser-window.md
[mousetrap]: https://github.com/ccampbell/mousetrap
[dom-events]: https://developer.mozilla.org/en-US/docs/Web/Events
[addEventListener-api]: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
[tutorial-starter-code]: tutorial-2-first-app.md#final-starter-code

View file

@ -1,76 +1,73 @@
---
title: Dock
description: Configure your application's Dock presence on macOS.
title: Dock Menu
description: Configure your app's Dock presence on macOS.
slug: macos-dock
hide_title: true
---
# Dock
# Dock Menu
Electron has APIs to configure the app's icon in the macOS Dock. A macOS-only
API exists to create a custom dock menu, but Electron also uses the app dock
icon as the entry point for cross-platform features like
[recent documents][recent-documents] and [application progress][progress-bar].
On macOS, the [Dock](https://support.apple.com/en-ca/guide/mac-help/mh35859/mac) is an interface
element that displays open and frequently-used apps. While opened or pinned, each app has its own
icon in the Dock.
The custom dock is commonly used to add shortcuts to tasks the user wouldn't
want to open the whole app window for.
> [!NOTE]
> On macOS, the Dock is the entry point for certain cross-platform features like
> [Recent Documents](./recent-documents.md) and [Application Progress](./progress-bar.md).
> Read those guides for more details.
**Dock menu of Terminal.app:**
## Dock API
![Dock Menu][dock-menu-image]
All functionality for the Dock is exposed via the [Dock](../api/dock.md) class exposed via
[`app.dock`](../api/app.md#appdock-macos-readonly) property. There is a single `Dock` instance per
Electron application, and this property only exists on macOS.
To set your custom dock menu, you need to use the
[`app.dock.setMenu`](../api/dock.md#docksetmenumenu-macos) API,
which is only available on macOS.
One of the main uses for your app's Dock icon is to expose additional app menus. The Dock menu is
triggered by right-clicking or <kbd>Ctrl</kbd>-clicking the app icon. By default, the app's Dock menu
will come with system-provided window management utilities, including the ability to show all windows,
hide the app, and switch betweeen different open windows.
```fiddle docs/fiddles/features/macos-dock-menu
To set an app-defined custom Dock menu, pass any [Menu](../api/menu.md) instance into the
[`dock.setMenu`](../api/dock.md#docksetmenumenu-macos) API.
> [!TIP]
> For best practices to make your Dock menu feel more native, see Apple's
> [Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/dock-menus)
> page on Dock menus.
## Attaching a context menu
```js title='Setting a Dock menu'
const { app, BrowserWindow, Menu } = require('electron/main')
function createWindow () {
const win = new BrowserWindow({
width: 800,
height: 600
})
win.loadFile('index.html')
}
const dockMenu = Menu.buildFromTemplate([
{
label: 'New Window',
click () { console.log('New Window') }
}, {
label: 'New Window with Settings',
submenu: [
{ label: 'Basic' },
{ label: 'Pro' }
]
},
{ label: 'New Command...' }
])
// dock.setMenu only works after the 'ready' event is fired
app.whenReady().then(() => {
const dockMenu = Menu.buildFromTemplate([
{
label: 'New Window',
click: () => { const win = new BrowserWindow() }
}
// add more menu options to the array
])
// Dock is undefined on platforms outside of macOS
// highlight-next-line
app.dock?.setMenu(dockMenu)
}).then(createWindow)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
```
After launching the Electron application, right click the application icon.
You should see the custom menu you just defined:
> [!NOTE]
> Unlike with regular [context menus](./context-menu.md), Dock context menus don't need to be
> manually instrumented using the `menu.popup` API. Instead, the Dock object handles click events
> for you.
![macOS dock menu](../images/macos-dock-menu.png)
> [!TIP]
> To learn more about crafting menus in Electron, see the [Menus](./menus.md#building-menus) guide.
[dock-menu-image]: https://cloud.githubusercontent.com/assets/639601/5069962/6032658a-6e9c-11e4-9953-aa84006bdfff.png
[recent-documents]: ./recent-documents.md
[progress-bar]: ./progress-bar.md
## Runnable Fiddle demo
Below is a runnable example of how you can use the Dock menu to create and close windows in your
Electron app.
```fiddle docs/fiddles/menus/dock-menu
```

348
docs/tutorial/menus.md Normal file
View file

@ -0,0 +1,348 @@
---
title: Menus
description: Configure cross-platform native OS menus with the Menu API.
slug: menus
hide_title: true
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import DocCardList from '@theme/DocCardList';
# Menus
Electron's [Menu](../api/menu.md) class provides a standardized way to create cross-platform native
menus throughout your application.
## Available menus in Electron
The same menu API is used for multiple use cases:
* The **application menu** is the top-level menu for your application. Each app only has a single
application menu at a time.
* **Context menus** are triggered by the user when right-clicking on a portion of your app's
interface.
* The **tray menu** is a special context menu triggered when right-clicking on your app's [Tray](../api/tray.md)
instance.
* On macOS, the **dock menu** is a special context menu triggered when right-clicking on your app's
icon in the system [Dock](https://support.apple.com/en-ca/guide/mac-help/mh35859/mac).
To learn more about the various kinds of native menus you can create and how to specify keyboard
shortcuts, see the individual guides in this section:
<DocCardList />
## Building menus
Each `Menu` instance is composed of an array of [MenuItem](../api/menu-item.md) objects accessible via
the `menu.items` instance property. Menus can be nested by setting the `item.submenu` property to
another menu.
There are two ways to build a menu: either by directly calling [`menu.append`](../api/menu.md#menuappendmenuitem)
or by using the static [`Menu.buildFromTemplate`](../api/menu.md#menubuildfromtemplatetemplate)
helper function.
The helper function reduces boilerplate by allowing you to pass a collection of `MenuItem`
constructor options (or instantiated `MenuItem` instances) in a single array rather than having to
append each item in a separate function call.
Below is an example of a minimal application menu:
<Tabs>
<TabItem value="constructor" label="Constructor">
```js title='menu.js'
const submenu = new Menu()
submenu.append(new MenuItem({ label: 'Hello' }))
submenu.append(new MenuItem({ type: 'separator' }))
submenu.append(new MenuItem({ label: 'Electron', type: 'checkbox', checked: true }))
const menu = new Menu()
menu.append(new MenuItem({ label: 'Menu', submenu }))
Menu.setApplicationMenu(menu)
```
</TabItem>
<TabItem value="template" label="Template Helper">
```js title='menu.js'
const menu = Menu.buildFromTemplate([{
label: 'Menu',
submenu: [
{ label: 'Hello' },
{ type: 'separator' },
{ label: 'Electron', type: 'checkbox', checked: true }
]
}])
Menu.setApplicationMenu(menu)
```
</TabItem>
</Tabs>
> [!IMPORTANT]
> All menu items (except for the `separator` type) must have a label. Labels can either be manually
> defined using the `label` property or inherited from the item's `role`.
### Types
A menu item's type grants it a particular appearance and functionality. Some types are
automatically assigned based on other constructor options:
* By default, menu items have the `normal` type.
* Menu items that contain the `submenu` property will be assigned the `submenu` type.
Other available types, when specified, give special additional properties to the menu item:
* `checkbox` - toggles the `checked` property whenever the menu item is clicked
* `radio` - toggles the `checked` property and turns off that property for all adjacent `radio` items
* `palette` - creates a [Palette](https://developer.apple.com/documentation/appkit/nsmenu/presentationstyle-swift.enum/palette)
submenu, which aligns items horizontally (available on macOS 14 and above)
* `header` - creates a section header, which can convey groupings with labels (available on macOS 14 and above)
> [!TIP]
> Adjacent `radio` items are at the same level of submenu and not divided by a separator.
>
> ```js
> [
> { type: 'radio', label: 'Adjacent 1' },
> { type: 'radio', label: 'Adjacent 2' },
> { type: 'separator' },
> { type: 'radio', label: 'Non-adjacent' } // unaffected by the others
> ]
> ```
### Roles
Roles give `normal` type menu items predefined behaviors.
We recommend specifying the `role` attribute for any menu item that matches a standard role
rather than trying to manually implement the behavior in a `click` function.
The built-in `role` behavior will give the best native experience.
The `label` and `accelerator` values are optional when using a `role` and will
default to appropriate values for each platform.
> [!TIP]
> Role strings are **case-insensitive**. For example, `toggleDevTools`, `toggledevtools`, and
> `TOGGLEDEVTOOLS` are all equivalent roles when defining menu items.
#### Edit roles
* `undo`
* `redo`
* `cut`
* `copy`
* `paste`
* `pasteAndMatchStyle`
* `selectAll`
* `delete`
#### Window roles
* `about` - Trigger a native about panel (custom message box on Window, which does not provide its own).
* `minimize` - Minimize current window.
* `close` - Close current window.
* `quit` - Quit the application.
* `reload` - Reload the current window.
* `forceReload` - Reload the current window ignoring the cache.
* `toggleDevTools` - Toggle developer tools in the current window.
* `togglefullscreen` - Toggle full screen mode on the current window.
* `resetZoom` - Reset the focused page's zoom level to the original size.
* `zoomIn` - Zoom in the focused page by 10%.
* `zoomOut` - Zoom out the focused page by 10%.
* `toggleSpellChecker` - Enable/disable built-in spellchecker.
#### Default menu roles
* `fileMenu` - The submenu is a "File" menu (Close / Quit)
* `editMenu` - The submenu is an "Edit" menu (Undo, Copy, etc.)
* `viewMenu` - The submenu is a "View" menu (Reload, Toggle Developer Tools, etc.)
* `windowMenu` - The submenu is a "Window" menu (Minimize, Zoom, etc.)
#### macOS-only roles
macOS has a number of platform-specific menu roles available. Many of these map to underlying
[AppKit](https://developer.apple.com/documentation/appkit) APIs.
##### App management roles
* `hide` - Map to the [`hide`](https://developer.apple.com/documentation/appkit/nsapplication/hide(_:)) action.
* `hideOthers` - Map to the [`hideOtherApplications`](https://developer.apple.com/documentation/appkit/nsapplication/hideotherapplications(_:)) action.
* `unhide` - Map to the [`unhideAllApplications`](https://developer.apple.com/documentation/appkit/nsapplication/unhideallapplications(_:)) action.
* `front` - Map to the [`arrangeInFront`](https://developer.apple.com/documentation/appkit/nsapplication/arrangeinfront(_:)) action.
* `zoom` - Map to the [`performZoom`](https://developer.apple.com/documentation/appkit/nswindow/performzoom(_:)) action.
##### Edit roles
* `showSubstitutions` - Map to the [`orderFrontSubstitutionsPanel`](https://developer.apple.com/documentation/appkit/nstextview/orderfrontsubstitutionspanel(_:)) action.
* `toggleSmartQuotes` - Map to the [`toggleAutomaticQuoteSubstitution`](https://developer.apple.com/documentation/appkit/nstextview/toggleautomaticquotesubstitution(_:)) action.
* `toggleSmartDashes` - Map to the [`toggleAutomaticDashSubstitution`](https://developer.apple.com/documentation/appkit/nstextview/toggleautomaticdashsubstitution(_:)) action.
* `toggleTextReplacement` - Map to the [`toggleAutomaticTextReplacement`](https://developer.apple.com/documentation/appkit/nstextview/toggleautomatictextreplacement(_:)) action.
##### Speech roles
* `startSpeaking` - Map to the [`startSpeaking`](https://developer.apple.com/documentation/appkit/nstextview/startspeaking(_:)) action.
* `stopSpeaking` - Map to the [`stopSpeaking`](https://developer.apple.com/documentation/appkit/nstextview/stopspeaking(_:)) action.
##### Native tab roles
* `toggleTabBar` - Map to the [`toggleTabBar`](https://developer.apple.com/documentation/appkit/nswindow/toggletabbar(_:)) action.
* `selectNextTab` - Map to the [`selectNextTab`](https://developer.apple.com/documentation/appkit/nswindow/selectnexttab(_:)) action.
* `selectPreviousTab` - Map to the [`selectPreviousTab`](https://developer.apple.com/documentation/appkit/nswindow/selectprevioustab(_:)) action.
<!-- * `showAllTabs` - Map to the `showAllTabs` action. -->
* `mergeAllWindows` - Map to the [`mergeAllWindows`](https://developer.apple.com/documentation/appkit/nswindow/mergeallwindows(_:)) action.
* `moveTabToNewWindow` - Map to the [`moveTabToNewWindow`](https://developer.apple.com/documentation/appkit/nswindow/movetabtonewwindow(_:)) action.
##### Default menu roles
* `appMenu` - Whole default "App" menu (About, Services, etc.)
* `services` - The submenu is a ["Services"](https://developer.apple.com/documentation/appkit/nsapplication/1428608-servicesmenu?language=objc) menu.
* `window` - The submenu is a "Window" menu.
* `help` - The submenu is a "Help" menu.
##### Other menu roles
* `recentDocuments` - The submenu is an "Open Recent" menu.
* `clearRecentDocuments` - Map to the [`clearRecentDocuments`](https://developer.apple.com/documentation/appkit/nsdocumentcontroller/clearrecentdocuments(_:)) action.
* `shareMenu` - The submenu is [share menu][ShareMenu]. The `sharingItem` property must also be set to indicate the item to share.
> [!IMPORTANT]
> When specifying a `role` on macOS, `label` and `accelerator` are the only
> options that will affect the menu item. All other options will be ignored.
### Accelerators
The `accelerator` property allows you to define accelerator strings to map menu items to keyboard
shortcuts. For more details, see the [Keyboard Shortcuts](./keyboard-shortcuts.md) guide.
## Advanced configuration
### Programmatic item positioning
You can make use of the `before`, `after`, `beforeGroupContaining`, `afterGroupContaining` and `id` attributes
to control how menu items will be placed when building a menu with `Menu.buildFromTemplate`.
* `before` - Inserts this item before the item with the specified id. If the
referenced item doesn't exist, the item will be inserted at the end of
the menu. Also implies that the menu item in question should be placed in the same “group” as the item.
* `after` - Inserts this item after the item with the specified id. If the
referenced item doesn't exist, the item will be inserted at the end of
the menu. Also implies that the menu item in question should be placed in the same “group” as the item.
* `beforeGroupContaining` - Provides a means for a single context menu to declare
the placement of their containing group before the containing group of the item with the specified id.
* `afterGroupContaining` - Provides a means for a single context menu to declare
the placement of their containing group after the containing group of the item with the specified id.
By default, items will be inserted in the order they exist in the template unless one of the specified
positioning keywords is used.
#### Examples
Template:
```js
[
{ id: '1', label: 'one' },
{ id: '2', label: 'two' },
{ id: '3', label: 'three' },
{ id: '4', label: 'four' }
]
```
Menu:
```plaintext
- one
- two
- three
- four
```
Template:
```js
[
{ id: '1', label: 'one' },
{ type: 'separator' },
{ id: '3', label: 'three', beforeGroupContaining: ['1'] },
{ id: '4', label: 'four', afterGroupContaining: ['2'] },
{ type: 'separator' },
{ id: '2', label: 'two' }
]
```
Menu:
```plaintext
- three
- four
- ---
- one
- ---
- two
```
Template:
```js
[
{ id: '1', label: 'one', after: ['3'] },
{ id: '2', label: 'two', before: ['1'] },
{ id: '3', label: 'three' }
]
```
Menu:
```plaintext
- ---
- three
- two
- one
```
### Icons
To add visual aid to your menus, you can use the `icon` property to assign images to individual
`MenuItem` instances.
```js title='Adding a little green circle to a menu item'
const { nativeImage } = require('electron/common')
const { MenuItem } = require('electron/main')
// highlight-next-line
const green = nativeImage.createFromDataURL('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAACOSURBVHgBpZLRDYAgEEOrEzgCozCCGzkCbKArOIlugJvgoRAUNcLRpvGH19TkgFQWkqIohhK8UEaKwKcsOg/+WR1vX+AlA74u6q4FqgCOSzwsGHCwbKliAF89Cv89tWmOT4VaVMoVbOBrdQUz+FrD6XItzh4LzYB1HFJ9yrEkZ4l+wvcid9pTssh4UKbPd+4vED2Nd54iAAAAAElFTkSuQmCC')
const item = new MenuItem({
label: 'Green Circle',
// highlight-next-line
icon: green
})
```
### Sublabels _macOS_
You can add sublabels (also known as [subtitles](https://developer.apple.com/documentation/appkit/nsmenuitem/subtitle))
to menu items using the `sublabel` option on macOS 14.4 and above.
```js title='Adding descriptions via sublabel'
const { MenuItem } = require('electron/main')
const item = new MenuItem({
label: 'Log Message',
// highlight-next-line
sublabel: 'This will use the console.log utility',
click: () => { console.log('Logging via menu...') }
})
```
### Tooltips _macOS_
Tooltips are informational indicators that appear when you hover over a menu item. You can set menu
item tooltips on macOS using the `toolTip` option.
```js title='Adding additional information via tooltip'
const { MenuItem } = require('electron/main')
const item = new MenuItem({
label: 'Hover Over Me',
// highlight-next-line
toolTip: 'This is additional info that appears on hover'
})
```

View file

@ -1,83 +1,86 @@
---
title: Tray
description: This guide will take you through the process of creating
a Tray icon with its own context menu to the system's notification area.
title: Tray Menu
description: Create a Tray icon with its own menu in the system's notification area.
slug: tray
hide_title: true
---
# Tray
# Tray Menu
## Overview
This guide will take you through the process of creating an icon with its own context menu to the
system tray.
<!-- ✍ Update this section if you want to provide more details -->
* On macOS, the icon will be located on the top right corner of your screen in the
[menu bar extras](https://developer.apple.com/design/human-interface-guidelines/the-menu-bar#Menu-bar-extras)
area.
* On Windows, the icon will be located in the [notification area](https://learn.microsoft.com/en-us/windows/win32/shell/notification-area)
at the end of the taskbar.
* On Linux, the location of the Tray will differ based on your desktop environment.
This guide will take you through the process of creating a
[Tray](../api/tray.md) icon with
its own context menu to the system's notification area.
## Creating a Tray icon
On MacOS and Ubuntu, the Tray will be located on the top
right corner of your screen, adjacent to your battery and wifi icons.
On Windows, the Tray will usually be located in the bottom right corner.
The tray icon for your Electron app needs to be created programmatically with an instance of
the [Tray](../api/tray.md#new-trayimage-guid) class. The class constructor requires a single
instance of a [NativeImage](../api/native-image.md#class-nativeimage) or a path to a compatible icon
file.
## Example
> [!NOTE]
> File formats vary per operating system. For more details, see the
> [Platform Considerations](../api/tray.md#platform-considerations) section of the Tray API documentation.
### main.js
## Minimizing to tray
First we must import `app`, `Tray`, `Menu`, `nativeImage` from `electron`.
In order to keep the app and the system tray icon alive even when all windows are closed, you need to
have a listener for the [`window-all-closed`](../api/app.md#event-window-all-closed) event on the
`app` module. The base Electron templates generally listen for this event but quit the app on
Windows and Linux to emulate standard OS behavior.
```js
const { app, Tray, Menu, nativeImage } = require('electron')
```
Next we will create our Tray. To do this, we will use a
[`NativeImage`](../api/native-image.md) icon,
which can be created through any one of these
[methods](../api/native-image.md#methods).
Note that we wrap our Tray creation code within an
[`app.whenReady`](../api/app.md#appwhenready)
as we will need to wait for our electron app to finish initializing.
```js title='main.js'
let tray
app.whenReady().then(() => {
const icon = nativeImage.createFromPath('path/to/asset.png')
tray = new Tray(icon)
// note: your contextMenu, Tooltip and Title code will go here!
```js title='Setting up minimize to tray'
app.on('window-all-closed', () => {
// having this listener active will prevent the app from quitting.
})
```
Great! Now we can start attaching a context menu to our Tray, like so:
## Attaching a context menu
```js @ts-expect-error=[8]
const contextMenu = Menu.buildFromTemplate([
{ label: 'Item1', type: 'radio' },
{ label: 'Item2', type: 'radio' },
{ label: 'Item3', type: 'radio', checked: true },
{ label: 'Item4', type: 'radio' }
])
You can attach a context menu to the Tray object by passing in a [Menu](../api/menu.md) instance
into the [`tray.setContextMenu`](../api/tray.md#traysetcontextmenumenu) function.
tray.setContextMenu(contextMenu)
> [!NOTE]
> Unlike with regular [context menus](./context-menu.md), Tray context menus don't need to be
> manually instrumented using the `menu.popup` API. Instead, the Tray object handles click events
> for you (although various click-related events exist on the API for advanced use cases).
```js title='Creating a Tray menu that can quit the app'
const { nativeImage } = require('electron/common')
const { app, Tray, Menu } = require('electron/main')
// save a reference to the Tray object globally to avoid garbage collection
let tray
// 16x16 red circle data URL
const icon = nativeImage.createFromDataURL('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAACTSURBVHgBpZKBCYAgEEV/TeAIjuIIbdQIuUGt0CS1gW1iZ2jIVaTnhw+Cvs8/OYDJA4Y8kR3ZR2/kmazxJbpUEfQ/Dm/UG7wVwHkjlQdMFfDdJMFaACebnjJGyDWgcnZu1/lrCrl6NCoEHJBrDwEr5NrT6ko/UV8xdLAC2N49mlc5CylpYh8wCwqrvbBGLoKGvz8Bfq0QPWEUo/EAAAAASUVORK5CYII=')
// The Tray can only be instantiated after the 'ready' event is fired
app.whenReady().then(() => {
tray = new Tray(icon)
const contextMenu = Menu.buildFromTemplate([
{ role: 'quit' }
])
tray.setContextMenu(contextMenu)
})
```
The code above will create 4 separate radio-type items in the context menu.
To read more about constructing native menus, click
[here](../api/menu.md#menubuildfromtemplatetemplate).
> [!TIP]
> To learn more about crafting menus in Electron, see the [Menus](./menus.md#building-menus) guide.
Finally, let's give our tray a tooltip and a title.
> [!WARNING]
> The `enabled` and `visibility` properties are not available for top-level menu items in the tray on macOS.
```js @ts-type={tray:Electron.Tray}
tray.setToolTip('This is my application')
tray.setTitle('This is my title')
```
## Conclusion
After you start your electron app, you should see the Tray residing
in either the top or bottom right of your screen, depending on your
operating system.
```fiddle docs/fiddles/native-ui/tray
## Runnable Fiddle demo
Below is a runnable example of attaching various menu items to the Tray's context menu that help
control app state and interact with the Tray API itself.
```fiddle docs/fiddles/menus/tray-menu
```

View file

@ -14,7 +14,6 @@ Windows, and Linux.
> supports composing many web views. `BaseWindow` can be used interchangeably with `BrowserWindow`
> in the examples of the documents in this section.
<!-- markdownlint-disable-next-line MD033 -->
<DocCardList />
[`BaseWindow`]: ../api/base-window.md