Aller au contenu

Window Menu

Ce contenu n’est pas encore disponible dans votre langue.

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. You can create various types of menu items including basic items, check items, and separators:

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

import { Menu } from '@tauri-apps/api/menu';
await using menu = await Menu.new({
items: [
{
id: 'quit',
text: 'Quit',
action: () => {
console.log('quit pressed');
},
},
{
id: 'check_item',
text: 'Check Item',
checked: true,
},
{
item: 'Separator',
},
{
id: 'disabled_item',
text: 'Disabled Item',
enabled: false,
},
{
id: 'status',
text: 'Status: Processing...',
},
],
});
// If a window was not created with an explicit menu or had one set explicitly,
// this menu will be assigned to it.
//
// NOTE: will be empty macOS. On macOS, all top-level entries must be SubMenus.
await menu.setAsAppMenu();
// Update individual menu item text
await using statusItem = await menu.get('status');
if (statusItem) {
await statusItem.setText('Status: Ready');
}

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';
await using 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.

This menu in the macOS menu bar
import { Menu, Submenu } from '@tauri-apps/api/menu';
// Will become the application submenu on MacOS
await using aboutSubmenu = await Submenu.new({
text: 'About',
items: [
{
id: 'quit',
text: 'Quit',
action: () => {
console.log('Quit pressed');
},
},
],
});
await using fileSubmenu = await Submenu.new({
text: 'File',
icon: 'Folder', // Optional: Add an icon to the submenu
items: [
{
id: 'new',
text: 'New',
action: () => {
console.log('New clicked');
},
},
{
id: 'open',
text: 'Open',
action: () => {
console.log('Open clicked');
},
},
{
id: 'save_as',
text: 'Save As...',
action: () => {
console.log('Save As clicked');
},
},
],
});
// Note: macOS injects additional "Writing Tools" elements into "Edit" menus
// (or, failing that, any menu with the "Copy" command).
await using editSubmenu = await Submenu.new({
text: 'Edit',
items: [
{
id: 'undo',
text: 'Undo',
action: () => {
console.log('Undo clicked');
},
},
{
id: 'redo',
text: 'Redo',
action: () => {
console.log('Redo clicked');
},
},
],
});
await using menu = await Menu.new({
items: [aboutSubmenu, fileSubmenu, editSubmenu],
});
await menu.setAsAppMenu();
// You can also update the submenu icon dynamically
await fileSubmenu.setIcon('document');
// Or set a native icon (only one type applies per platform)
await fileSubmenu.setNativeIcon('NSFolder');

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

import { Menu, PredefinedMenuItem, Submenu } from '@tauri-apps/api/menu';
// macOS injects additional "Writing Tools" menu items into your "Edit" menu,
// or, if that's missing, whichever menu has this copy item:
await using copy = await PredefinedMenuItem.new({
text: 'copy-text',
item: 'Copy',
});
await using separator = await PredefinedMenuItem.new({
text: 'separator-text',
item: 'Separator',
});
await using undo = await PredefinedMenuItem.new({
text: 'undo-text',
item: 'Undo',
});
await using redo = await PredefinedMenuItem.new({
text: 'redo-text',
item: 'Redo',
});
await using cut = await PredefinedMenuItem.new({
text: 'cut-text',
item: 'Cut',
});
await using paste = await PredefinedMenuItem.new({
text: 'paste-text',
item: 'Paste',
});
await using select_all = await PredefinedMenuItem.new({
text: 'select_all-text',
item: 'SelectAll',
});
// Becomes the application submenu on MacOS
await using about = await Submenu.new({ text: 'About', items: [] });
await using predefined = await Submenu.new({
text: 'Predefined',
items: [copy, separator, undo, redo, cut, paste, select_all],
});
await using menu = await Menu.new({
items: [about, predefined],
});
await menu.setAsAppMenu();

The result:

This menu in the macOS menu bar

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,
Submenu,
} from '@tauri-apps/api/menu';
import { Image } from '@tauri-apps/api/image';
let currentLanguage = 'en';
// No `await using`: these items are used inside their `action` callbacks, so must not be freed at end of scope.
//
// You must `.close()` them manually later.
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 === 'zh');
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
await using 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');
},
});
// Becomes the application submenu on MacOS
await using about = await Submenu.new({ text: 'About', items: [] });
await using change_menu = await Submenu.new({
id: 'change_menu',
text: 'change_menu',
items: [text_item, check_sub_item_en, check_sub_item_zh, icon_item],
});
await using menu = await Menu.new({
items: [about, change_menu],
});
await menu.setAsAppMenu();

The menu looks like this:

This menu in the macOS menu bar

There’re a few gotchas you should be aware of when using the JavaScript menu APIs, mainly because JavaScript does not have any way to run cleanup code when an object is being removed by garbage collection:

  • The menus will not clean up automatically unless close is called or closed by await using after the binding variable goes out of scope.
  • You should close the menu items you got e.g. from Menu.get, the await using syntax makes it easier, for example: await using status = menu.get('status') Be careful with APIs that can return a Menu such as Menu.setAsAppMenu, you’ll need to close them when you’re done with them or if you don’t plan to use them.
  • You should close the menu resource whenever you don’t need to access it in JavaScript anymore.

For example:

const copy = await PredefinedMenuItem.new({ item: 'Copy' });
const menu = await Menu.new({ items: [copy] });
// `copy` is now managed by the `menu` and the JavaScript resource reference can be closed
await copy.close();
const prev = await menu.setAsAppMenu();
// `menu` is now set as the app menu, we can close the JavaScript resource reference
await menu.close();
// We don't plan to use the previously set menu, close it here.
await prev?.close();

© 2026 Tauri Contributors. CC-BY / MIT