Skip to content
Tauri

Window Menu

Native application menus can be attached to both to a window or system tray. Available on desktop.

To create a base-level native window menu, and attach to a window:

Use the Menu.new static function to create a window menu:

import { Menu } from '@tauri-apps/api/menu';
const menu = await Menu.new({
items: [
{
id: 'quit',
text: 'Quit',
action: () => {
console.log('quit pressed');
},
},
],
});
// If a window was not created with an explicit menu or had one set explicitly,
// this menu will be assigned to it.
menu.setAsAppMenu().then((res) => {
console.log('menu set success', res);
});

Each custom menu item triggers an event when clicked. Use the on_menu_event API to handle them.

import { Menu } from '@tauri-apps/api/menu';
const menu = await Menu.new({
items: [
{
id: 'Open',
text: 'open',
action: () => {
console.log('open pressed');
},
},
{
id: 'Close',
text: 'close',
action: () => {
console.log('close pressed');
},
},
],
});
await menu.setAsAppMenu();

Multi-level menus allow you to group menu items under categories like “File,” “Edit,” etc. These will appear as part of the application window for Windows or Linux, or in the menu bar on MacOS.

Note: When using submenus on MacOS, all items must be grouped under a submenu. Top-level items will be ignored. Additionally, the first submenu will be placed under the application’s about menu by default, regardless of the text label. You should include a submenu as the first entry (say, an “About” submenu) to fill this space.

import { Menu, MenuItem, Submenu } from '@tauri-apps/api/menu';
// Will become the application submenu on MacOS
const aboutSubmenu = await Submenu.new({
text: 'About',
items: [
await MenuItem.new({
id: 'quit',
text: 'Quit',
action: () => {
console.log('Quit pressed');
},
}),
],
});
const fileSubmenu = await Submenu.new({
text: 'File',
items: [
await MenuItem.new({
id: 'new',
text: 'New',
action: () => {
console.log('New clicked');
},
}),
await MenuItem.new({
id: 'open',
text: 'Open',
action: () => {
console.log('Open clicked');
},
}),
await MenuItem.new({
id: 'save_as',
text: 'Save As...',
action: () => {
console.log('Save As clicked');
},
}),
],
});
const editSubmenu = await Submenu.new({
text: 'Edit',
items: [
await MenuItem.new({
id: 'undo',
text: 'Undo',
action: () => {
console.log('Undo clicked');
},
}),
await MenuItem.new({
id: 'redo',
text: 'Redo',
action: () => {
console.log('Redo clicked');
},
}),
],
});
const menu = await Menu.new({
items: [aboutSubmenu, fileSubmenu, editSubmenu],
});
menu.setAsAppMenu();

To use built-in (native) menu items that has predefined behavior by the operating system or Tauri:

import { Menu, PredefinedMenuItem } from '@tauri-apps/api/menu';
const copy = await PredefinedMenuItem.new({
text: 'copy-text',
item: 'Copy',
});
const separator = await PredefinedMenuItem.new({
text: 'separator-text',
item: 'Separator',
});
const undo = await PredefinedMenuItem.new({
text: 'undo-text',
item: 'Undo',
});
const redo = await PredefinedMenuItem.new({
text: 'redo-text',
item: 'Redo',
});
const cut = await PredefinedMenuItem.new({
text: 'cut-text',
item: 'Cut',
});
const paste = await PredefinedMenuItem.new({
text: 'paste-text',
item: 'Paste',
});
const select_all = await PredefinedMenuItem.new({
text: 'select_all-text',
item: 'SelectAll',
});
const menu = await Menu.new({
items: [copy, separator, undo, redo, cut, paste, select_all],
});
await menu.setAsAppMenu();

If you want to change the status of the menu, such as text, icon, or check status, you can set_menu again:

import {
Menu,
CheckMenuItem,
IconMenuItem,
MenuItem,
} from '@tauri-apps/api/menu';
import { Image } from '@tauri-apps/api/image';
let currentLanguage = 'en';
const check_sub_item_en = await CheckMenuItem.new({
id: 'en',
text: 'English',
checked: currentLanguage === 'en',
action: () => {
currentLanguage = 'en';
check_sub_item_en.setChecked(currentLanguage === 'en');
check_sub_item_zh.setChecked(currentLanguage === 'cn');
console.log('English pressed');
},
});
const check_sub_item_zh = await CheckMenuItem.new({
id: 'zh',
text: 'Chinese',
checked: currentLanguage === 'zh',
action: () => {
currentLanguage = 'zh';
check_sub_item_en.setChecked(currentLanguage === 'en');
check_sub_item_zh.setChecked(currentLanguage === 'zh');
check_sub_item_zh.setAccelerator('Ctrl+L');
console.log('Chinese pressed');
},
});
// Load icon from path
const icon = await Image.fromPath('../src/icon.png');
const icon2 = await Image.fromPath('../src/icon-2.png');
const icon_item = await IconMenuItem.new({
id: 'icon_item',
text: 'Icon Item',
icon: icon,
action: () => {
icon_item.setIcon(icon2);
console.log('icon pressed');
},
});
const text_item = await MenuItem.new({
id: 'text_item',
text: 'Text Item',
action: () => {
text_item.setText('Text Item Changed');
console.log('text pressed');
},
});
const menu = await Menu.new({
items: [
{
id: 'change menu',
text: 'change_menu',
items: [text_item, check_sub_item_en, check_sub_item_zh, icon_item],
},
],
});
await menu.setAsAppMenu();

© 2025 Tauri Contributors. CC-BY / MIT