TypeScript/JavaScript API bindings for Tauri applications providing comprehensive desktop app functionality
—
The menu system provides comprehensive native menu functionality including menu bars, context menus, and system tray menus. It supports different menu item types with platform-specific behavior optimizations.
Create and manage application menus with support for hierarchical structures.
/**
* Main menu class for creating menu bars and context menus
*/
class Menu {
/**
* Create a new menu
* @param opts - Menu creation options
* @returns Promise resolving to Menu instance
*/
static new(opts?: MenuOptions): Promise<Menu>;
/**
* Create a default application menu
* @returns Promise resolving to Menu instance with platform defaults
*/
static default(): Promise<Menu>;
/**
* Add menu items to the end of this menu
* @param items - Single item or array of items to add
*/
append<T extends MenuItemType>(items: T | T[]): Promise<void>;
/**
* Add menu items to the beginning of this menu
* @param items - Single item or array of items to add
*/
prepend<T extends MenuItemType>(items: T | T[]): Promise<void>;
/**
* Insert menu items at specific position
* @param items - Single item or array of items to insert
* @param position - Zero-based position to insert at
*/
insert<T extends MenuItemType>(items: T | T[], position: number): Promise<void>;
/**
* Remove a menu item from this menu
* @param item - Menu item instance to remove
*/
remove(item: MenuItemInstance): Promise<void>;
/**
* Remove menu item at specific position
* @param position - Zero-based position to remove
* @returns Removed menu item or null
*/
removeAt(position: number): Promise<MenuItemInstance | null>;
/**
* Get all menu items in this menu
* @returns Array of menu item instances
*/
items(): Promise<MenuItemInstance[]>;
/**
* Find menu item by ID
* @param id - Menu item identifier
* @returns Menu item instance or null if not found
*/
get(id: string): Promise<MenuItemInstance | null>;
/**
* Show this menu as a context menu
* @param at - Position to show menu (relative to window)
* @param window - Target window (defaults to current)
*/
popup(at?: Position, window?: Window): Promise<void>;
/**
* Set this menu as the application-wide menu
* @returns Previous app menu or null
*/
setAsAppMenu(): Promise<Menu | null>;
/**
* Set this menu as a window-specific menu (Windows/Linux only)
* @param window - Target window (defaults to current)
* @returns Previous window menu or null
*/
setAsWindowMenu(window?: Window): Promise<Menu | null>;
}
interface MenuOptions {
/** Menu identifier */
id?: string;
/** Initial menu items */
items?: MenuItemType[];
}Usage Examples:
import { Menu, MenuItem, Submenu } from '@tauri-apps/api/menu';
// Create basic menu
const menu = await Menu.new({
items: [
{ text: 'File', items: [
{ text: 'New', action: () => console.log('New file') },
{ text: 'Open', action: () => console.log('Open file') },
{ text: '---' }, // Separator
{ text: 'Exit', action: () => console.log('Exit') }
]},
{ text: 'Edit', items: [
{ text: 'Cut', accelerator: 'CmdOrCtrl+X' },
{ text: 'Copy', accelerator: 'CmdOrCtrl+C' },
{ text: 'Paste', accelerator: 'CmdOrCtrl+V' }
]}
]
});
// Set as application menu
await menu.setAsAppMenu();
// Create context menu
const contextMenu = await Menu.new({
items: [
{ text: 'Copy', action: () => navigator.clipboard.writeText('data') },
{ text: 'Paste', action: async () => {
const text = await navigator.clipboard.readText();
console.log('Pasted:', text);
}}
]
});
// Show context menu at mouse position
document.addEventListener('contextmenu', async (e) => {
e.preventDefault();
await contextMenu.popup({ x: e.clientX, y: e.clientY });
});Different types of menu items for various use cases.
/**
* Standard menu item with text and action
*/
class MenuItem {
static new(opts: MenuItemOptions): Promise<MenuItem>;
text(): Promise<string>;
setText(text: string): Promise<void>;
isEnabled(): Promise<boolean>;
setEnabled(enabled: boolean): Promise<void>;
}
interface MenuItemOptions {
/** Menu item identifier */
id?: string;
/** Display text */
text: string;
/** Whether item is enabled */
enabled?: boolean;
/** Keyboard accelerator (e.g., 'CmdOrCtrl+N') */
accelerator?: string;
/** Action to execute when clicked */
action?: () => void;
}
/**
* Menu item with checkbox state
*/
class CheckMenuItem {
static new(opts: CheckMenuItemOptions): Promise<CheckMenuItem>;
isChecked(): Promise<boolean>;
setChecked(checked: boolean): Promise<void>;
}
interface CheckMenuItemOptions extends MenuItemOptions {
/** Initial checked state */
checked?: boolean;
}
/**
* Menu item with icon
*/
class IconMenuItem {
static new(opts: IconMenuItemOptions): Promise<IconMenuItem>;
setIcon(icon: Image | string): Promise<void>;
}
interface IconMenuItemOptions extends MenuItemOptions {
/** Menu item icon */
icon: Image | string;
}
/**
* Submenu containing other menu items
*/
class Submenu {
static new(opts: SubmenuOptions): Promise<Submenu>;
append<T extends MenuItemType>(items: T | T[]): Promise<void>;
prepend<T extends MenuItemType>(items: T | T[]): Promise<void>;
insert<T extends MenuItemType>(items: T | T[], position: number): Promise<void>;
items(): Promise<MenuItemInstance[]>;
}
interface SubmenuOptions {
/** Submenu identifier */
id?: string;
/** Submenu text */
text: string;
/** Whether submenu is enabled */
enabled?: boolean;
/** Child menu items */
items?: MenuItemType[];
}
/**
* Platform-specific predefined menu items
*/
class PredefinedMenuItem {
static new(opts: PredefinedMenuItemOptions): Promise<PredefinedMenuItem>;
}
interface PredefinedMenuItemOptions {
/** Predefined item type */
item: PredefinedMenuItemType;
/** Override default text */
text?: string;
}
type PredefinedMenuItemType =
| 'Copy'
| 'Cut'
| 'Paste'
| 'SelectAll'
| 'Undo'
| 'Redo'
| 'Minimize'
| 'Hide'
| 'HideOthers'
| 'ShowAll'
| 'CloseWindow'
| 'Quit'
| 'About'
| '---'; // SeparatorUsage Examples:
import { MenuItem, CheckMenuItem, IconMenuItem, Submenu, PredefinedMenuItem } from '@tauri-apps/api/menu';
// Standard menu item
const newItem = await MenuItem.new({
text: 'New Document',
accelerator: 'CmdOrCtrl+N',
action: () => createNewDocument()
});
// Checkbox menu item
const darkModeItem = await CheckMenuItem.new({
text: 'Dark Mode',
checked: true,
action: () => toggleDarkMode()
});
// Icon menu item
const saveItem = await IconMenuItem.new({
text: 'Save',
icon: '/icons/save.png',
accelerator: 'CmdOrCtrl+S',
action: () => saveDocument()
});
// Submenu
const fileSubmenu = await Submenu.new({
text: 'File',
items: [
{ text: 'New', action: () => console.log('New') },
{ text: 'Open', action: () => console.log('Open') },
{ text: '---' }, // Separator
{ text: 'Recent', items: [
{ text: 'file1.txt' },
{ text: 'file2.txt' }
]}
]
});
// Predefined menu items
const copyItem = await PredefinedMenuItem.new({ item: 'Copy' });
const separator = await PredefinedMenuItem.new({ item: '---' });
const quitItem = await PredefinedMenuItem.new({
item: 'Quit',
text: 'Exit Application' // Override default text
});Modify menus at runtime based on application state.
import { Menu, MenuItem } from '@tauri-apps/api/menu';
// Create dynamic menu that updates based on state
async function createDynamicMenu() {
const menu = await Menu.new();
// Add initial items
await menu.append([
{ text: 'File', items: [] },
{ text: 'Edit', items: [] }
]);
return menu;
}
// Update menu based on application state
async function updateMenuForState(menu: Menu, hasOpenFile: boolean) {
// Get File submenu
const fileSubmenu = await menu.get('file-menu') as Submenu;
if (hasOpenFile) {
// Add file-specific items
await fileSubmenu.append([
{ text: 'Save', accelerator: 'CmdOrCtrl+S' },
{ text: 'Save As...', accelerator: 'CmdOrCtrl+Shift+S' },
{ text: 'Close', accelerator: 'CmdOrCtrl+W' }
]);
} else {
// Remove file-specific items
const items = await fileSubmenu.items();
for (const item of items) {
if (['Save', 'Save As...', 'Close'].includes(await item.text())) {
await fileSubmenu.remove(item);
}
}
}
}
// Enable/disable menu items
async function setMenuItemState(menu: Menu, itemId: string, enabled: boolean) {
const item = await menu.get(itemId);
if (item && 'setEnabled' in item) {
await item.setEnabled(enabled);
}
}Handle platform differences in menu systems.
import { Menu, Submenu } from '@tauri-apps/api/menu';
async function createPlatformOptimizedMenu() {
const menu = await Menu.new();
// macOS typically has app name as first menu
if (navigator.platform.includes('Mac')) {
const appSubmenu = await Submenu.new({
text: 'MyApp',
items: [
{ item: 'About' },
{ item: '---' },
{ text: 'Preferences', accelerator: 'Cmd+,' },
{ item: '---' },
{ item: 'Hide' },
{ item: 'HideOthers' },
{ item: 'ShowAll' },
{ item: '---' },
{ item: 'Quit' }
]
});
await menu.append(appSubmenu);
}
// File menu
const fileSubmenu = await Submenu.new({
text: 'File',
items: [
{ text: 'New', accelerator: 'CmdOrCtrl+N' },
{ text: 'Open', accelerator: 'CmdOrCtrl+O' },
{ item: '---' },
{ text: 'Save', accelerator: 'CmdOrCtrl+S' }
]
});
await menu.append(fileSubmenu);
// Window menu (common on macOS)
if (navigator.platform.includes('Mac')) {
const windowSubmenu = await Submenu.new({
text: 'Window',
items: [
{ item: 'Minimize' },
{ item: 'CloseWindow' }
]
});
await menu.append(windowSubmenu);
}
return menu;
}Handle menu item clicks and state changes.
import { Menu, CheckMenuItem } from '@tauri-apps/api/menu';
// Create menu with event handlers
async function createMenuWithEvents() {
const menu = await Menu.new({
items: [
{
text: 'View',
items: [
{
text: 'Show Sidebar',
checked: true,
action: async () => {
const item = await menu.get('sidebar-toggle') as CheckMenuItem;
const isChecked = await item.isChecked();
// Toggle sidebar visibility
const sidebar = document.getElementById('sidebar');
if (sidebar) {
sidebar.style.display = isChecked ? 'block' : 'none';
}
console.log('Sidebar:', isChecked ? 'shown' : 'hidden');
}
},
{
text: 'Zoom In',
accelerator: 'CmdOrCtrl+Plus',
action: () => {
document.body.style.zoom = String(parseFloat(document.body.style.zoom || '1') + 0.1);
}
},
{
text: 'Zoom Out',
accelerator: 'CmdOrCtrl+-',
action: () => {
document.body.style.zoom = String(Math.max(0.5, parseFloat(document.body.style.zoom || '1') - 0.1));
}
},
{
text: 'Reset Zoom',
accelerator: 'CmdOrCtrl+0',
action: () => {
document.body.style.zoom = '1';
}
}
]
}
]
});
return menu;
}type MenuItemType =
| MenuItemOptions
| CheckMenuItemOptions
| IconMenuItemOptions
| SubmenuOptions
| PredefinedMenuItemOptions;
type MenuItemInstance =
| MenuItem
| CheckMenuItem
| IconMenuItem
| Submenu
| PredefinedMenuItem;
type Position = LogicalPosition | PhysicalPosition | { x: number; y: number };Menu operations can fail due to platform limitations or invalid parameters:
import { Menu, MenuItem } from '@tauri-apps/api/menu';
try {
const menu = await Menu.new();
await menu.append({ text: 'Test Item' });
await menu.setAsAppMenu();
} catch (error) {
// Common error scenarios:
// - Invalid menu structure
// - Platform doesn't support operation
// - Menu item not found
// - Permission denied
console.error('Menu operation failed:', error);
}get() to find menu items instead of iterating through items()Menu.default() as a starting point for standard menusInstall with Tessl CLI
npx tessl i tessl/npm-tauri-apps--api