- ⚡️ Quick start for creating menubar applications using Electron.
- 🚀 Zero runtime dependencies.
- 💻 Works on macOS, Windows and most Linux distributions. See tested platforms.
![]() |
![]() |
![]() |
|---|---|---|
| macOS | Windows 10 | Ubuntu |
Note
On native Wayland, the desktop compositor controls window placement, so the popover cannot be anchored to the tray icon and is positioned by the desktop (usually centered). This is a Wayland limitation, not something the library can override. Tray-relative positioning works under X11/XWayland. See window positioning on Linux for the detail and a workaround.
pnpm add electron-menubarStarting with your own new project, run these commands:
$ pnpm add electron-menubar
$ touch myApp.js
$ touch index.htmlFill index.html with some HTML, and myApp.js like this:
const { menubar } = require('electron-menubar');
const mb = menubar();
mb.on('ready', () => {
console.log('app is ready');
// your app code here
});Then use electron to run the app:
$ electron myApp.jsSee examples folder for a selection of working examples.
The return value of menubar() is a Menubar class instance, which exposes the following properties and methods.
| Property | Description |
|---|---|
app |
The Electron App instance. |
window |
The Electron Browser Window instance. |
tray |
The Electron Tray instance. |
positioner |
The Positioner instance used to compute the window's on-screen coordinates. |
| Method | Description |
|---|---|
setOption(option, value) |
Change an option after menubar is created. |
getOption(option) |
Get a menubar option. |
showWindow() |
Show the menubar window. |
hideWindow() |
Hide the menubar window. |
toggleWindow() |
Show the window if hidden, hide it if visible. |
recenterOnTray() |
Re-center the window over the tray icon. |
setContextMenu(menu) |
Replace the tray context menu (auto-re-publishes on Linux). |
setGlobalShortcut(accelerator) |
Register a global accelerator that toggles the window. Returns false on registration failure. |
destroy() |
Tear down the menubar instance. |
isDestroyed() |
Whether the menubar is currently destroyed. |
You can pass an optional options object into the menubar({ ... }) function:
| Option | Default | Description |
|---|---|---|
dir |
process.cwd() |
The app source directory. |
index |
file:// + opts.dir + index.html |
The URL to load the menubar's browserWindow with. The url can be a remote address (e.g. http://) or a path to a local HTML file using the file:// protocol. |
browserWindow |
BrowserWindow options passed to the BrowserWindow constructor, see Electron docs. Useful fields•x (default undefined) - the x position of the window• y (default undefined) - the y position of the window• width (default 400) - window width• height (default 400) - window height• alwaysOnTop (default false) - if true, the window will not hide on blur |
|
icon |
opts.dir + IconTemplate.png |
The png icon to use for the menubar. A good size to start with is 20x20. To support retina, supply a 2x sized image (e.g. 40x40) with @2x added to the end of the name, so icon.png and icon@2x.png, and Electron will automatically use your @2x version on retina screens. |
tooltip |
empty | Menubar tray icon tooltip text. |
tray |
created on-the-fly | An Electron Tray instance. If provided, opts.icon will be ignored. |
preloadWindow |
false |
Create BrowserWindow instance before it is used, increasing resource usage but making the click on the menubar load faster. |
loadUrlOptions |
undefined |
The options passed when loading the index URL in the menubar's browserWindow. Everything browserWindow.loadURL supports is supported; this object is simply passed onto browserWindow.loadURL. |
showOnAllWorkspaces |
true |
Makes the window available on all macOS workspaces. |
windowPosition |
trayCenter (macOS/Linux), trayBottomCenter (Windows) |
Sets the window position (browserWindow.x / browserWindow.y will still override this). Valid values: trayLeft, trayBottomLeft, trayRight, trayBottomRight, trayCenter, trayBottomCenter, topLeft, topRight, bottomLeft, bottomRight, topCenter, bottomCenter, leftCenter, rightCenter, center. |
showDockIcon |
false |
Configure the visibility of the application dock icon, macOS only. Hiding reliablyWhenfalse, the library calls app.dock.hide() at startup and re-asserts it once shortly after, because macOS can silently drop the underlying process transform when it races the launch activation. The transform only runs after the process has already launched as a regular dock app, so the tile briefly exists on every launch and hiding it stays best-effort. Packaged apps that never want a dock tile should also declare LSUIElement in their Info.plist (with electron-builder: mac.extendInfo.LSUIElement: true), so the process starts as an agent app and no tile is ever created. showDockIcon: false then still covers development runs, where the stock Electron binary's plist is not under your control. |
trigger |
'click' |
Tray event that toggles the menubar window. One of 'click', 'right-click', or 'none'. Use 'none' to disable automatic toggling, useful when a single tray icon serves multiple windows. The window can still be shown by calling mb.showWindow() directly. |
showOnRightClick |
false |
Deprecated, use trigger: 'right-click' instead. Show the window on right-click event instead of regular click. |
contextMenu |
An Electron Menu to attach to the tray icon. Platform behaviorOn Linux it is bound viatray.setContextMenu (required by libappindicator / StatusNotifierItem) and re-published on every show/hide to defeat the indicator's menu cache. On macOS and Windows it pops up on right-click via tray.popUpContextMenu, so left-click continues to toggle the window. Combine with trigger: 'none' if you want right-click to be the only interaction. |
|
hideOnClose |
false |
Hide the window on close instead of destroying it, so the next tray click re-uses the same BrowserWindow. Platform notesOn Linux/Wayland the hide is deferred viasetImmediate to work around a compositor bug that leaves frameless surfaces in a half-closed state when hidden synchronously from the close handler. The library tracks the app's before-quit event internally, so real quits go through unimpeded. |
escapeToHide |
false |
Hide the menubar window when the user presses Escape while it has focus. |
ignoreDoubleClickEvents |
true (macOS only) |
Calls tray.setIgnoreDoubleClickEvents(true) so an accidental double-click doesn't race the close-on-blur handler and flicker the tray icon. Pass false to opt out. No-op on Linux/Windows. |
globalShortcut |
An Accelerator string registered as a global keyboard shortcut that toggles the menubar window. Unregistered automatically on destroy(). Use mb.setGlobalShortcut(accelerator) to change or clear it at runtime. |
The Menubar class is an event emitter:
| Event | Description |
|---|---|
ready |
When menubar's tray icon has been created and initialized, i.e. when menubar is ready to be used. Note: this is different from the Electron app's ready event, which happens much earlier in the process. |
create-window |
The line before new BrowserWindow() is called. |
before-load |
After create window, before loadUrl (can be used for require("@electron/remote/main").enable(webContents)). |
after-create-window |
The line after all window init code is done and the url was loaded. |
show |
The line before window.show() is called. |
after-show |
The line after window.show() is called. |
hide |
The line before window.hide() is called (on window blur). |
after-hide |
The line after window.hide() is called. |
after-close |
After the .window (BrowserWindow) property has been deleted. |
focus-lost |
Emitted if the always-on-top option is set and the user clicks away. |
- Use
mb.on('after-create-window', callback)to run things after your app has loaded. For example you could runmb.window.openDevTools()to open the developer tools for debugging, or load a different URL withmb.window.loadURL() - Use
mb.on('focus-lost')if you would like to perform some operation when using the optionbrowserWindow.alwaysOnTop: true - To restore focus of previous window after menubar hide, use
mb.on('after-hide', () => { mb.app.hide() } )or similar - To attach a native context menu, pass it as
contextMenu:menubar({ contextMenu }). The library wires it viasetContextMenuon Linux andpopUpContextMenuon right-click on macOS/Windows so left-click still toggles the window. See this example for more information. - To avoid a flash when opening your menubar app, you can disable backgrounding the app using the following:
mb.app.commandLine.appendSwitch('disable-backgrounding-occluded-windows', 'true');
Originally created by Max — hard-forked from max-mapper/menubar.


