npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

electron-menubar

v11.1.1

Published

high level way to create menubar desktop applications with electron

Downloads

3,116

Readme

Features

  • ⚡️ 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.

Why electron-menubar?

electron-menubar builds on max-mapper/menubar, with fixes and controls developed for Gitify. The original provides the tray-app foundation. This fork brings more of the platform-specific behavior into the library so applications have less to implement themselves.

| Area | What this fork adds | | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Windows focus and positioning | Ignores transient blur events immediately after opening, keeps resized windows clear of the taskbar, handles left-side taskbars, and prevents recursive resize events during repositioning. | | Windows tray overflow | Raises popups above the hidden-icons panel automatically and still dismisses on click-away. Apps use the same keep-open option on every platform. Available since 11.0.0. | | macOS | Opens on the active Space when another app is fullscreen, hides the Dock icon again after Electron's startup race, and ignores tray double-clicks by default to avoid flicker. | | Linux tray menus | Publishes native tray context menus and provides refreshContextMenu() to update them after menu items change. Documents native Wayland positioning limits and the XWayland workaround. | | Window controls | Built-in hideOnClose, escapeToHide, configurable tray triggers, global shortcuts, toggleWindow(), and recenterOnTray(). Closing can hide the window while real quits and updater restarts still work. | | Instance cleanup | destroy() removes the instance's listeners and unregisters its global shortcut. | | Packaging | Zero runtime dependencies, including a built-in positioner. Ships CommonJS and ESM entry points with TypeScript declarations. | | Verification | Unit tests, native Electron end-to-end tests on macOS, Windows, and Linux, and visual checks that the tray icon and window render. The platform report includes screenshots and positioning limitations. |

Popup behavior without platform checks

Menubar manages popup stacking and focus on every platform. A plain menubar() dismisses on click-away and opens above the Windows hidden-icons panel. To keep it open, change one option:

const mb = menubar();

mb.setOption('hideOnBlur', false); // Keep open and on top.
mb.setOption('hideOnBlur', true); // Resume click-away dismissal.

Opening DevTools temporarily keeps the popup open. Closing DevTools restores the latest hideOnBlur preference. Options set before window creation also apply when the window is recreated.

Migrating from 10.x

These defaults apply starting with 11.0.0. Previously, menubar inferred dismissal from window.isAlwaysOnTop(). Use hideOnBlur: false for persistent popups instead of browserWindow.alwaysOnTop: true or runtime window.setAlwaysOnTop(true) calls. browserWindow.alwaysOnTop now controls stacking only; it does not disable dismissal. Setting hideOnBlur: false also keeps the popup on top on macOS and Linux.

Remove Windows pop-up-menu workarounds and DevTools focus handlers from apps. Menubar owns stacking during window creation, showing, preference changes, and DevTools transitions. Apps can still customize DevTools dimensions and layout. See the changelog for released changes and the API documentation for all options.

Installation

pnpm add electron-menubar

Usage

Starting with your own new project, run these commands:

$ pnpm add electron-menubar
$ touch myApp.js
$ touch index.html

Fill 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.js

Examples

See examples folder for a selection of working examples.

API Documentation

Menubar Class

The return value of menubar() is a Menubar class instance, which exposes the following properties and methods.

Properties

| 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. |

Methods

| 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). | | refreshContextMenu() | Re-publish the current context menu after mutating its items in place. Required on Linux, a no-op elsewhere. | | 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. |

menubar() Options

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) - keeps the window on top without changing click-away dismissal; Windows popups are always raised above the tray | | 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 [email protected], 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. On macOS the window leaves all workspaces while hidden and rejoins them on showWindow(), so show it with showWindow() rather than window.show() or window.showInactive(). | | 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 reliablyWhen false, 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 via tray.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. | | hideOnBlur | true | Set false to keep the popup open and on top, emitting focus-lost on click-away. Defaults to true. DevTools temporarily suspends dismissal without changing this preference. Supports runtime changes through setOption. | | 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 via setImmediate 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 and the auto updater's before-quit-for-update event internally, so real quits and update restarts 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. |

Events

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 on click-away when hideOnBlur is false or DevTools is open. |

Tips

  • Use mb.on('after-create-window', callback) to run things after your app has loaded. For example you could run mb.window.openDevTools() to open the developer tools for debugging, or load a different URL with mb.window.loadURL()
  • Use mb.on('focus-lost') if you would like to perform some operation when using the option hideOnBlur: false
  • 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 via setContextMenu on Linux and popUpContextMenu on 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');

Acknowledgements

Originally created by Max — hard-forked from max-mapper/menubar.