0
# Menu System
1
2
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.
3
4
## Capabilities
5
6
### Menu Creation and Management
7
8
Create and manage application menus with support for hierarchical structures.
9
10
```typescript { .api }
11
/**
12
* Main menu class for creating menu bars and context menus
13
*/
14
class Menu {
15
/**
16
* Create a new menu
17
* @param opts - Menu creation options
18
* @returns Promise resolving to Menu instance
19
*/
20
static new(opts?: MenuOptions): Promise<Menu>;
21
22
/**
23
* Create a default application menu
24
* @returns Promise resolving to Menu instance with platform defaults
25
*/
26
static default(): Promise<Menu>;
27
28
/**
29
* Add menu items to the end of this menu
30
* @param items - Single item or array of items to add
31
*/
32
append<T extends MenuItemType>(items: T | T[]): Promise<void>;
33
34
/**
35
* Add menu items to the beginning of this menu
36
* @param items - Single item or array of items to add
37
*/
38
prepend<T extends MenuItemType>(items: T | T[]): Promise<void>;
39
40
/**
41
* Insert menu items at specific position
42
* @param items - Single item or array of items to insert
43
* @param position - Zero-based position to insert at
44
*/
45
insert<T extends MenuItemType>(items: T | T[], position: number): Promise<void>;
46
47
/**
48
* Remove a menu item from this menu
49
* @param item - Menu item instance to remove
50
*/
51
remove(item: MenuItemInstance): Promise<void>;
52
53
/**
54
* Remove menu item at specific position
55
* @param position - Zero-based position to remove
56
* @returns Removed menu item or null
57
*/
58
removeAt(position: number): Promise<MenuItemInstance | null>;
59
60
/**
61
* Get all menu items in this menu
62
* @returns Array of menu item instances
63
*/
64
items(): Promise<MenuItemInstance[]>;
65
66
/**
67
* Find menu item by ID
68
* @param id - Menu item identifier
69
* @returns Menu item instance or null if not found
70
*/
71
get(id: string): Promise<MenuItemInstance | null>;
72
73
/**
74
* Show this menu as a context menu
75
* @param at - Position to show menu (relative to window)
76
* @param window - Target window (defaults to current)
77
*/
78
popup(at?: Position, window?: Window): Promise<void>;
79
80
/**
81
* Set this menu as the application-wide menu
82
* @returns Previous app menu or null
83
*/
84
setAsAppMenu(): Promise<Menu | null>;
85
86
/**
87
* Set this menu as a window-specific menu (Windows/Linux only)
88
* @param window - Target window (defaults to current)
89
* @returns Previous window menu or null
90
*/
91
setAsWindowMenu(window?: Window): Promise<Menu | null>;
92
}
93
94
interface MenuOptions {
95
/** Menu identifier */
96
id?: string;
97
/** Initial menu items */
98
items?: MenuItemType[];
99
}
100
```
101
102
**Usage Examples:**
103
104
```typescript
105
import { Menu, MenuItem, Submenu } from '@tauri-apps/api/menu';
106
107
// Create basic menu
108
const menu = await Menu.new({
109
items: [
110
{ text: 'File', items: [
111
{ text: 'New', action: () => console.log('New file') },
112
{ text: 'Open', action: () => console.log('Open file') },
113
{ text: '---' }, // Separator
114
{ text: 'Exit', action: () => console.log('Exit') }
115
]},
116
{ text: 'Edit', items: [
117
{ text: 'Cut', accelerator: 'CmdOrCtrl+X' },
118
{ text: 'Copy', accelerator: 'CmdOrCtrl+C' },
119
{ text: 'Paste', accelerator: 'CmdOrCtrl+V' }
120
]}
121
]
122
});
123
124
// Set as application menu
125
await menu.setAsAppMenu();
126
127
// Create context menu
128
const contextMenu = await Menu.new({
129
items: [
130
{ text: 'Copy', action: () => navigator.clipboard.writeText('data') },
131
{ text: 'Paste', action: async () => {
132
const text = await navigator.clipboard.readText();
133
console.log('Pasted:', text);
134
}}
135
]
136
});
137
138
// Show context menu at mouse position
139
document.addEventListener('contextmenu', async (e) => {
140
e.preventDefault();
141
await contextMenu.popup({ x: e.clientX, y: e.clientY });
142
});
143
```
144
145
### Menu Item Types
146
147
Different types of menu items for various use cases.
148
149
```typescript { .api }
150
/**
151
* Standard menu item with text and action
152
*/
153
class MenuItem {
154
static new(opts: MenuItemOptions): Promise<MenuItem>;
155
156
text(): Promise<string>;
157
setText(text: string): Promise<void>;
158
isEnabled(): Promise<boolean>;
159
setEnabled(enabled: boolean): Promise<void>;
160
}
161
162
interface MenuItemOptions {
163
/** Menu item identifier */
164
id?: string;
165
/** Display text */
166
text: string;
167
/** Whether item is enabled */
168
enabled?: boolean;
169
/** Keyboard accelerator (e.g., 'CmdOrCtrl+N') */
170
accelerator?: string;
171
/** Action to execute when clicked */
172
action?: () => void;
173
}
174
175
/**
176
* Menu item with checkbox state
177
*/
178
class CheckMenuItem {
179
static new(opts: CheckMenuItemOptions): Promise<CheckMenuItem>;
180
181
isChecked(): Promise<boolean>;
182
setChecked(checked: boolean): Promise<void>;
183
}
184
185
interface CheckMenuItemOptions extends MenuItemOptions {
186
/** Initial checked state */
187
checked?: boolean;
188
}
189
190
/**
191
* Menu item with icon
192
*/
193
class IconMenuItem {
194
static new(opts: IconMenuItemOptions): Promise<IconMenuItem>;
195
196
setIcon(icon: Image | string): Promise<void>;
197
}
198
199
interface IconMenuItemOptions extends MenuItemOptions {
200
/** Menu item icon */
201
icon: Image | string;
202
}
203
204
/**
205
* Submenu containing other menu items
206
*/
207
class Submenu {
208
static new(opts: SubmenuOptions): Promise<Submenu>;
209
210
append<T extends MenuItemType>(items: T | T[]): Promise<void>;
211
prepend<T extends MenuItemType>(items: T | T[]): Promise<void>;
212
insert<T extends MenuItemType>(items: T | T[], position: number): Promise<void>;
213
items(): Promise<MenuItemInstance[]>;
214
}
215
216
interface SubmenuOptions {
217
/** Submenu identifier */
218
id?: string;
219
/** Submenu text */
220
text: string;
221
/** Whether submenu is enabled */
222
enabled?: boolean;
223
/** Child menu items */
224
items?: MenuItemType[];
225
}
226
227
/**
228
* Platform-specific predefined menu items
229
*/
230
class PredefinedMenuItem {
231
static new(opts: PredefinedMenuItemOptions): Promise<PredefinedMenuItem>;
232
}
233
234
interface PredefinedMenuItemOptions {
235
/** Predefined item type */
236
item: PredefinedMenuItemType;
237
/** Override default text */
238
text?: string;
239
}
240
241
type PredefinedMenuItemType =
242
| 'Copy'
243
| 'Cut'
244
| 'Paste'
245
| 'SelectAll'
246
| 'Undo'
247
| 'Redo'
248
| 'Minimize'
249
| 'Hide'
250
| 'HideOthers'
251
| 'ShowAll'
252
| 'CloseWindow'
253
| 'Quit'
254
| 'About'
255
| '---'; // Separator
256
```
257
258
**Usage Examples:**
259
260
```typescript
261
import { MenuItem, CheckMenuItem, IconMenuItem, Submenu, PredefinedMenuItem } from '@tauri-apps/api/menu';
262
263
// Standard menu item
264
const newItem = await MenuItem.new({
265
text: 'New Document',
266
accelerator: 'CmdOrCtrl+N',
267
action: () => createNewDocument()
268
});
269
270
// Checkbox menu item
271
const darkModeItem = await CheckMenuItem.new({
272
text: 'Dark Mode',
273
checked: true,
274
action: () => toggleDarkMode()
275
});
276
277
// Icon menu item
278
const saveItem = await IconMenuItem.new({
279
text: 'Save',
280
icon: '/icons/save.png',
281
accelerator: 'CmdOrCtrl+S',
282
action: () => saveDocument()
283
});
284
285
// Submenu
286
const fileSubmenu = await Submenu.new({
287
text: 'File',
288
items: [
289
{ text: 'New', action: () => console.log('New') },
290
{ text: 'Open', action: () => console.log('Open') },
291
{ text: '---' }, // Separator
292
{ text: 'Recent', items: [
293
{ text: 'file1.txt' },
294
{ text: 'file2.txt' }
295
]}
296
]
297
});
298
299
// Predefined menu items
300
const copyItem = await PredefinedMenuItem.new({ item: 'Copy' });
301
const separator = await PredefinedMenuItem.new({ item: '---' });
302
const quitItem = await PredefinedMenuItem.new({
303
item: 'Quit',
304
text: 'Exit Application' // Override default text
305
});
306
```
307
308
### Dynamic Menu Management
309
310
Modify menus at runtime based on application state.
311
312
```typescript
313
import { Menu, MenuItem } from '@tauri-apps/api/menu';
314
315
// Create dynamic menu that updates based on state
316
async function createDynamicMenu() {
317
const menu = await Menu.new();
318
319
// Add initial items
320
await menu.append([
321
{ text: 'File', items: [] },
322
{ text: 'Edit', items: [] }
323
]);
324
325
return menu;
326
}
327
328
// Update menu based on application state
329
async function updateMenuForState(menu: Menu, hasOpenFile: boolean) {
330
// Get File submenu
331
const fileSubmenu = await menu.get('file-menu') as Submenu;
332
333
if (hasOpenFile) {
334
// Add file-specific items
335
await fileSubmenu.append([
336
{ text: 'Save', accelerator: 'CmdOrCtrl+S' },
337
{ text: 'Save As...', accelerator: 'CmdOrCtrl+Shift+S' },
338
{ text: 'Close', accelerator: 'CmdOrCtrl+W' }
339
]);
340
} else {
341
// Remove file-specific items
342
const items = await fileSubmenu.items();
343
for (const item of items) {
344
if (['Save', 'Save As...', 'Close'].includes(await item.text())) {
345
await fileSubmenu.remove(item);
346
}
347
}
348
}
349
}
350
351
// Enable/disable menu items
352
async function setMenuItemState(menu: Menu, itemId: string, enabled: boolean) {
353
const item = await menu.get(itemId);
354
if (item && 'setEnabled' in item) {
355
await item.setEnabled(enabled);
356
}
357
}
358
```
359
360
### Platform-Specific Behavior
361
362
Handle platform differences in menu systems.
363
364
```typescript
365
import { Menu, Submenu } from '@tauri-apps/api/menu';
366
367
async function createPlatformOptimizedMenu() {
368
const menu = await Menu.new();
369
370
// macOS typically has app name as first menu
371
if (navigator.platform.includes('Mac')) {
372
const appSubmenu = await Submenu.new({
373
text: 'MyApp',
374
items: [
375
{ item: 'About' },
376
{ item: '---' },
377
{ text: 'Preferences', accelerator: 'Cmd+,' },
378
{ item: '---' },
379
{ item: 'Hide' },
380
{ item: 'HideOthers' },
381
{ item: 'ShowAll' },
382
{ item: '---' },
383
{ item: 'Quit' }
384
]
385
});
386
await menu.append(appSubmenu);
387
}
388
389
// File menu
390
const fileSubmenu = await Submenu.new({
391
text: 'File',
392
items: [
393
{ text: 'New', accelerator: 'CmdOrCtrl+N' },
394
{ text: 'Open', accelerator: 'CmdOrCtrl+O' },
395
{ item: '---' },
396
{ text: 'Save', accelerator: 'CmdOrCtrl+S' }
397
]
398
});
399
await menu.append(fileSubmenu);
400
401
// Window menu (common on macOS)
402
if (navigator.platform.includes('Mac')) {
403
const windowSubmenu = await Submenu.new({
404
text: 'Window',
405
items: [
406
{ item: 'Minimize' },
407
{ item: 'CloseWindow' }
408
]
409
});
410
await menu.append(windowSubmenu);
411
}
412
413
return menu;
414
}
415
```
416
417
### Menu Events and Actions
418
419
Handle menu item clicks and state changes.
420
421
```typescript
422
import { Menu, CheckMenuItem } from '@tauri-apps/api/menu';
423
424
// Create menu with event handlers
425
async function createMenuWithEvents() {
426
const menu = await Menu.new({
427
items: [
428
{
429
text: 'View',
430
items: [
431
{
432
text: 'Show Sidebar',
433
checked: true,
434
action: async () => {
435
const item = await menu.get('sidebar-toggle') as CheckMenuItem;
436
const isChecked = await item.isChecked();
437
438
// Toggle sidebar visibility
439
const sidebar = document.getElementById('sidebar');
440
if (sidebar) {
441
sidebar.style.display = isChecked ? 'block' : 'none';
442
}
443
444
console.log('Sidebar:', isChecked ? 'shown' : 'hidden');
445
}
446
},
447
{
448
text: 'Zoom In',
449
accelerator: 'CmdOrCtrl+Plus',
450
action: () => {
451
document.body.style.zoom = String(parseFloat(document.body.style.zoom || '1') + 0.1);
452
}
453
},
454
{
455
text: 'Zoom Out',
456
accelerator: 'CmdOrCtrl+-',
457
action: () => {
458
document.body.style.zoom = String(Math.max(0.5, parseFloat(document.body.style.zoom || '1') - 0.1));
459
}
460
},
461
{
462
text: 'Reset Zoom',
463
accelerator: 'CmdOrCtrl+0',
464
action: () => {
465
document.body.style.zoom = '1';
466
}
467
}
468
]
469
}
470
]
471
});
472
473
return menu;
474
}
475
```
476
477
## Type Definitions
478
479
```typescript { .api }
480
type MenuItemType =
481
| MenuItemOptions
482
| CheckMenuItemOptions
483
| IconMenuItemOptions
484
| SubmenuOptions
485
| PredefinedMenuItemOptions;
486
487
type MenuItemInstance =
488
| MenuItem
489
| CheckMenuItem
490
| IconMenuItem
491
| Submenu
492
| PredefinedMenuItem;
493
494
type Position = LogicalPosition | PhysicalPosition | { x: number; y: number };
495
```
496
497
## Error Handling
498
499
Menu operations can fail due to platform limitations or invalid parameters:
500
501
```typescript
502
import { Menu, MenuItem } from '@tauri-apps/api/menu';
503
504
try {
505
const menu = await Menu.new();
506
await menu.append({ text: 'Test Item' });
507
await menu.setAsAppMenu();
508
} catch (error) {
509
// Common error scenarios:
510
// - Invalid menu structure
511
// - Platform doesn't support operation
512
// - Menu item not found
513
// - Permission denied
514
console.error('Menu operation failed:', error);
515
}
516
```
517
518
## Best Practices
519
520
### Performance
521
- Cache menu instances rather than recreating them
522
- Use `get()` to find menu items instead of iterating through `items()`
523
- Batch menu modifications when possible
524
525
### User Experience
526
- Use standard accelerators (Ctrl+C, Ctrl+V, etc.)
527
- Follow platform conventions for menu organization
528
- Provide visual feedback for checkable items
529
- Use separators to group related functionality
530
531
### Platform Integration
532
- Use `Menu.default()` as a starting point for standard menus
533
- Test menu behavior on all target platforms
534
- Handle platform-specific limitations gracefully
535
- Use predefined menu items when available for better integration