Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/webviewjs/webview/llms.txt

Use this file to discover all available pages before exploring further.

WebviewJS uses muda to render native menu bars on all three desktop platforms. Menus are described as plain JavaScript objects — a tree of MenuItemOptions entries that can include labels, keyboard accelerators, predefined system roles, and nested submenus. Custom menu item clicks are reported through a single application-level event so that all windows share the same handler. The menu system integrates naturally with the rest of WebviewJS: set a global menu on app, or pass a per-window menu option when creating a BrowserWindow.

Setting a Global Application Menu

app.setMenu({
  items: [
    {
      label: 'File',
      submenu: {
        items: [
          { id: 'file-new',  label: 'New',  accelerator: 'CmdOrCtrl+N' },
          { id: 'file-open', label: 'Open', accelerator: 'CmdOrCtrl+O' },
          { role: 'separator' },
          { role: 'quit' },
        ],
      },
    },
    {
      label: 'Edit',
      submenu: {
        items: [
          { role: 'undo' },
          { role: 'redo' },
          { role: 'separator' },
          { role: 'cut' },
          { role: 'copy' },
          { role: 'paste' },
          { role: 'selectall' },
        ],
      },
    },
  ],
});
app.setMenu() is additive — calling it again replaces the previous menu entirely. Pass null to remove the global menu.

app.setMenu(options?)

options
MenuOptions | null | undefined
The new global menu configuration. Pass null or undefined to remove the current menu.

Per-Window Menu

Override the global menu for a specific window by passing a menu option to createBrowserWindow(), or by calling win.setMenu() on an existing window:
// At creation time
const win = app.createBrowserWindow({
  title: 'Editor',
  menu: { items: [ /* … */ ] },
});

// Or at runtime
win.setMenu({ items: [ /* … */ ] });
BrowserWindowOptions.menu
MenuOptions
Attaches a menu to this window only, overriding the global menu.
BrowserWindowOptions.showMenu
boolean
When true, the global application menu is shown on this window even if a per-window menu has been set.

id
string
Stable identifier emitted in the custom-menu-click application event. Should be unique across all menu items. Items without an id (e.g. separators, role-only items) do not fire custom-menu-click.
label
string
Display text for the menu item. Not required for separator or purely role-based items.
enabled
boolean
Controls whether the item is interactive. Defaults to true. Set to false to render a greyed-out item.
accelerator
string
Keyboard shortcut string. Uses a cross-platform syntax (see Accelerator Syntax below). Examples: "CmdOrCtrl+N", "Alt+F4", "Shift+CmdOrCtrl+Z", "F5".
submenu
MenuOptions
Nested MenuOptions that opens as a child menu when this item is hovered or activated. When a submenu is present, id and accelerator are ignored — the item acts purely as a parent container.
role
string
Assigns a predefined native behavior to the item. The platform localizes the label automatically. See Predefined Roles for the full list.

Predefined Roles

Roles map directly to native platform actions and are automatically localized. Use them instead of custom id/label entries for standard OS behaviors.
RoleAction
copyCopy selection to clipboard
pastePaste from clipboard
cutCut selection to clipboard
undoUndo last action
redoRedo last undone action
selectall / select-allSelect all content
separator / -Horizontal separator line
minimizeMinimise the window
maximizeMaximise the window
fullscreenToggle fullscreen mode
close / closewindow / close-windowClose the active window
quitQuit the application
aboutShow the about dialog
hideHide the application (macOS only)
hideothers / hide-othersHide other applications (macOS only)
showall / show-allShow all hidden applications (macOS only)
servicesOpen the Services submenu (macOS only)
bringalltofront / bring-all-to-frontBring all windows to front (macOS only)
Role-based items handle their own click behaviour natively. You do not need to listen for custom-menu-click to handle copy, paste, quit, and similar roles.

Handling Menu Clicks

Custom menu items (those with an id) fire the custom-menu-click event on the Application instance:
app.on('custom-menu-click', ({ customMenuEvent }) => {
  console.log('Menu item clicked:', customMenuEvent.id);
  console.log('From window ID:',    customMenuEvent.windowId);

  switch (customMenuEvent.id) {
    case 'file-new':
      createNewDocument();
      break;
    case 'file-open':
      const files = win.openFileDialog({ title: 'Select a file' });
      openFiles(files);
      break;
    case 'quit':
      app.exit();
      break;
  }
});
customMenuEvent.id
string
The id set on the MenuItemOptions that was clicked.
customMenuEvent.windowId
number
Numeric identifier of the window whose menu bar the item belongs to.

Accelerator Syntax

Accelerators follow a Modifier+Key pattern. Multiple modifiers are joined with +. Key names match standard key identifiers.
TokenMeaning
CmdOrCtrlCmd on macOS, Ctrl on Windows and Linux
CtrlControl key (all platforms)
AltAlt / Option key
ShiftShift key
Meta / SuperWindows key / Command key
F1F12Function keys
Letter / digitLiteral key (e.g. N, S, 0)
CmdOrCtrl+N          → New file
CmdOrCtrl+Shift+S    → Save As
Alt+F4               → Close (Windows convention)
F12                  → Developer Tools
CmdOrCtrl+,          → Preferences (macOS convention)

Platform Notes

macOS uses a single shared menu bar at the top of the screen that belongs to the frontmost application. The global menu set via app.setMenu() is displayed for all windows. Per-window menus are possible but override the shared bar only when that window is active.
Each window has its own menu bar attached to its title bar. Both global and per-window menus are rendered as a standard Win32 menu bar.
Per-window GTK menu bars are attached through Tao’s GTK window container. Behaviour depends on the desktop environment; some environments (such as those using global menu applets) may redirect the menu bar.

Full Example

import { Application } from '@webviewjs/webview';

const app = new Application();

// Handle menu item clicks
app.on('custom-menu-click', ({ customMenuEvent: e }) => {
  console.log(`Menu item clicked: "${e.id}" (window ${e.windowId})`);

  switch (e.id) {
    case 'new':       console.log('Creating new document…'); break;
    case 'open':      console.log('Opening file…');          break;
    case 'save':      console.log('Saving file…');           break;
    case 'quit':      app.exit();                            break;
    case 'reload':    webview.reload();                      break;
    case 'devtools':  webview.openDevtools();                break;
    case 'about':     console.log('About this app…');        break;
  }
});

app.setMenu({
  items: [
    {
      label: 'File',
      submenu: {
        items: [
          { id: 'new',     label: 'New',      accelerator: 'CmdOrCtrl+N' },
          { id: 'open',    label: 'Open…',    accelerator: 'CmdOrCtrl+O' },
          { role: 'separator' },
          { id: 'save',    label: 'Save',     accelerator: 'CmdOrCtrl+S' },
          { id: 'save-as', label: 'Save As…', accelerator: 'CmdOrCtrl+Shift+S' },
          { role: 'separator' },
          { id: 'quit',    label: 'Quit',     accelerator: 'CmdOrCtrl+Q' },
        ],
      },
    },
    {
      label: 'Edit',
      submenu: {
        items: [
          { role: 'cut' },
          { role: 'copy' },
          { role: 'paste' },
          { role: 'selectall' },
          { role: 'separator' },
          { id: 'preferences', label: 'Preferences…', accelerator: 'CmdOrCtrl+,' },
        ],
      },
    },
    {
      label: 'View',
      submenu: {
        items: [
          { id: 'reload',   label: 'Reload',           accelerator: 'CmdOrCtrl+R' },
          { id: 'devtools', label: 'Developer Tools',  accelerator: 'F12' },
          { role: 'separator' },
          {
            label: 'Zoom',
            submenu: {
              items: [
                { id: 'zoom-in',    label: 'Zoom In',     accelerator: 'CmdOrCtrl+Plus' },
                { id: 'zoom-out',   label: 'Zoom Out',    accelerator: 'CmdOrCtrl+-' },
                { id: 'zoom-reset', label: 'Actual Size', accelerator: 'CmdOrCtrl+0' },
              ],
            },
          },
        ],
      },
    },
    {
      label: 'Help',
      submenu: {
        items: [
          { id: 'about', label: 'About' },
          { id: 'docs',  label: 'Documentation', accelerator: 'F1' },
        ],
      },
    },
  ],
});

app.on('application-close-requested', () => app.exit());

const window  = app.createBrowserWindow({ title: 'Menu Example', width: 800, height: 600 });
const webview = window.createWebview({ html: '<h1>Menu Example</h1>' });

app.run();

Build docs developers (and LLMs) love