---
title: Menu Reference
description: Complete reference for menu item types, properties, and methods
sidebar:
  order: 4
---

import { Tabs, TabItem } from "@astrojs/starlight/components";

## Menu Reference

Complete reference for menu item types, properties, and dynamic behaviour. Build professional, responsive menus with checkboxes, radio groups, separators, and dynamic updates.

## Menu Item Types

### Regular Menu Items

The most common type—displays text and triggers an action:

```go
menuItem := menu.Add("Click Me")
menuItem.OnClick(func(ctx *application.Context) {
    fmt.Println("Menu item clicked!")
})
```

**Use for:** Commands, actions, opening windows

### Checkboxes

Toggle-able menu items with checked/unchecked state:

```go
checkbox := menu.AddCheckbox("Enable Feature", true)  // true = initially checked
checkbox.OnClick(func(ctx *application.Context) {
    isChecked := ctx.ClickedMenuItem().Checked()
    fmt.Printf("Feature is now: %v\n", isChecked)
})
```

**Use for:** Boolean settings, feature toggles, view options

**Important:** The checked state toggles automatically when clicked.

### Radio Groups

Mutually exclusive options—only one can be selected:

```go
menu.AddRadio("Small", true)   // true = initially selected
menu.AddRadio("Medium", false)
menu.AddRadio("Large", false)
```

**Use for:** Mutually exclusive choices (size, theme, mode)

**How grouping works:**
- Adjacent radio items form a group automatically
- Selecting one deselects others in the group
- Separate groups with a separator or regular item

**Example with multiple groups:**

```go
// Group 1: Size
menu.AddRadio("Small", true)
menu.AddRadio("Medium", false)
menu.AddRadio("Large", false)

menu.AddSeparator()

// Group 2: Theme
menu.AddRadio("Light", true)
menu.AddRadio("Dark", false)
```

### Submenus

Nested menu structures for organisation:

```go
submenu := menu.AddSubmenu("More Options")
submenu.Add("Submenu Item 1").OnClick(func(ctx *application.Context) {
    // Handle click
})
submenu.Add("Submenu Item 2")
```

**Use for:** Grouping related items, reducing clutter

**Nesting limit:** Most platforms support 2-3 levels. Avoid deeper nesting.

### Separators

Visual dividers between menu items:

```go
menu.Add("Item 1")
menu.AddSeparator()
menu.Add("Item 2")
```

**Use for:** Grouping related items visually

**Best practice:** Don't start or end menus with separators.

## Menu Item Properties

### Label

The text displayed for the menu item:

```go
menuItem := menu.Add("Initial Label")
menuItem.SetLabel("New Label")

// Get current label
label := menuItem.Label()
```

**Dynamic labels:**

```go
updateMenuItem := menu.Add("Check for Updates")
updateMenuItem.OnClick(func(ctx *application.Context) {
    updateMenuItem.SetLabel("Checking...")
    menu.Update()  // Important on Windows!
    
    // Perform update check
    checkForUpdates()
    
    updateMenuItem.SetLabel("Check for Updates")
    menu.Update()
})
```

### Enabled State

Control whether the menu item can be interacted with:

```go
menuItem := menu.Add("Save")
menuItem.SetEnabled(false)  // Greyed out, can't click

// Enable it later
menuItem.SetEnabled(true)
menu.Update()  // Important: Call this after changing enabled state!

// Check current state
isEnabled := menuItem.Enabled()
```

:::caution[Windows Menu Behaviour]
On Windows, menus need to be reconstructed when their state changes. **Always call `menu.Update()` after enabling/disabling menu items**, especially if the item was created whilst disabled.

**Why:** Windows menus are rebuilt from scratch when updated. If you don't call `Update()`, click handlers won't fire properly.
:::

**Example: Dynamic enable/disable**

```go
var hasSelection bool

cutMenuItem := menu.Add("Cut")
cutMenuItem.SetEnabled(false)  // Initially disabled

copyMenuItem := menu.Add("Copy")
copyMenuItem.SetEnabled(false)

// When selection changes
func onSelectionChanged(selected bool) {
    hasSelection = selected
    cutMenuItem.SetEnabled(hasSelection)
    copyMenuItem.SetEnabled(hasSelection)
    menu.Update()  // Critical on Windows!
}
```

**Common pattern: Enable on condition**

```go
saveMenuItem := menu.Add("Save")

func updateSaveMenuItem() {
    canSave := hasUnsavedChanges() && !isSaving()
    saveMenuItem.SetEnabled(canSave)
    menu.Update()
}

// Call whenever state changes
onDocumentChanged(func() {
    updateSaveMenuItem()
})
```

### Checked State

For checkbox and radio items, control or query their checked state:

```go
checkbox := menu.AddCheckbox("Feature", false)
checkbox.SetChecked(true)
menu.Update()

// Query state
isChecked := checkbox.Checked()
```

**Auto-toggle:** Checkboxes toggle automatically when clicked. You don't need to call `SetChecked()` in the click handler.

**Manual control:**

```go
checkbox := menu.AddCheckbox("Auto-save", false)

// Sync with external state
func syncAutoSave(enabled bool) {
    checkbox.SetChecked(enabled)
    menu.Update()
}
```

### Accelerators (Keyboard Shortcuts)

Add keyboard shortcuts to menu items:

```go
saveMenuItem := menu.Add("Save")
saveMenuItem.SetAccelerator("CmdOrCtrl+S")

quitMenuItem := menu.Add("Quit")
quitMenuItem.SetAccelerator("CmdOrCtrl+Q")
```

**Accelerator format:**
- `CmdOrCtrl` - Cmd on macOS, Ctrl on Windows/Linux
- `Shift`, `Alt`, `Option` - Modifier keys
- `A-Z`, `0-9` - Letter/number keys
- `F1-F12` - Function keys
- `Enter`, `Space`, `Backspace`, etc. - Special keys

**Examples:**

```go
"CmdOrCtrl+S"           // Save
"CmdOrCtrl+Shift+S"     // Save As
"CmdOrCtrl+W"           // Close Window
"CmdOrCtrl+Q"           // Quit
"F5"                    // Refresh
"CmdOrCtrl+,"           // Preferences (macOS convention)
"Alt+F4"                // Close (Windows convention)
```

**Platform-specific accelerators:**

```go
if runtime.GOOS == "darwin" {
    prefsMenuItem.SetAccelerator("Cmd+,")
} else {
    prefsMenuItem.SetAccelerator("Ctrl+P")
}
```

### Tooltip

Add hover text to menu items (platform support varies):

```go
menuItem := menu.Add("Advanced Options")
menuItem.SetTooltip("Configure advanced settings")
```

**Platform support:**
- **Windows:** ✅ Supported
- **macOS:** ❌ Not supported (tooltips not standard for menus)
- **Linux:** ⚠️ Varies by desktop environment

### Hidden State

Hide menu items without removing them:

```go
debugMenuItem := menu.Add("Debug Mode")
debugMenuItem.SetHidden(true)  // Hidden

// Show in debug builds
if isDebugBuild {
    debugMenuItem.SetHidden(false)
    menu.Update()
}
```

**Use for:** Debug options, feature flags, conditional features

## Event Handling

### OnClick Handler

Execute code when menu item is clicked:

```go
menuItem := menu.Add("Click Me")
menuItem.OnClick(func(ctx *application.Context) {
    // Handle click
    fmt.Println("Clicked!")
})
```

**Context provides:**
- `ctx.ClickedMenuItem()` - The menu item that was clicked
- Window context (if from window menu)
- Application context

**Example: Access menu item in handler**

```go
checkbox := menu.AddCheckbox("Feature", false)
checkbox.OnClick(func(ctx *application.Context) {
    item := ctx.ClickedMenuItem()
    isChecked := item.Checked()
    fmt.Printf("Feature is now: %v\n", isChecked)
})
```

### Multiple Handlers

You can set multiple handlers (last one wins):

```go
menuItem := menu.Add("Action")
menuItem.OnClick(func(ctx *application.Context) {
    fmt.Println("First handler")
})

// This replaces the first handler
menuItem.OnClick(func(ctx *application.Context) {
    fmt.Println("Second handler - this one runs")
})
```

**Best practice:** Set handler once, use conditional logic inside if needed.

## Dynamic Menus

### Updating Menu Items

**The golden rule:** Always call `menu.Update()` after changing menu state.

```go
// ✅ Correct
menuItem.SetEnabled(true)
menu.Update()

// ❌ Wrong (especially on Windows)
menuItem.SetEnabled(true)
// Forgot to call Update() - click handlers may not work!
```

**Why this matters:**
- **Windows:** Menus are reconstructed when updated
- **macOS/Linux:** Less critical but still recommended
- **Click handlers:** Won't fire properly without Update()

### Rebuilding Menus

For major changes, rebuild the entire menu:

```go
func rebuildFileMenu() {
    menu := app.Menu.New()
    
    menu.Add("New").OnClick(handleNew)
    menu.Add("Open").OnClick(handleOpen)
    
    if hasRecentFiles() {
        recentMenu := menu.AddSubmenu("Open Recent")
        for _, file := range getRecentFiles() {
            recentMenu.Add(file).OnClick(func(ctx *application.Context) {
                openFile(file)
            })
        }
    }
    
    menu.AddSeparator()
    menu.Add("Quit").OnClick(handleQuit)
    
    // Set the new menu
    window.SetMenu(menu)
}
```

**When to rebuild:**
- Recent files list changes
- Plugin menus change
- Major state transitions

**When to update:**
- Enable/disable items
- Change labels
- Toggle checkboxes

### Context-Sensitive Menus

Adjust menus based on application state:

```go
func updateEditMenu() {
    cutMenuItem.SetEnabled(hasSelection())
    copyMenuItem.SetEnabled(hasSelection())
    pasteMenuItem.SetEnabled(hasClipboardContent())
    undoMenuItem.SetEnabled(canUndo())
    redoMenuItem.SetEnabled(canRedo())
    menu.Update()
}

// Call whenever state changes
onSelectionChanged(updateEditMenu)
onClipboardChanged(updateEditMenu)
onUndoStackChanged(updateEditMenu)
```

## Platform Differences

### Menu Bar Location

| Platform | Location | Notes |
|----------|----------|-------|
| **macOS** | Top of screen | Global menu bar |
| **Windows** | Top of window | Per-window menu |
| **Linux** | Top of window | Per-window (usually) |

### Standard Menus

**macOS:**
- Has "Application" menu (with app name)
- "Preferences" in Application menu
- "Quit" in Application menu

**Windows/Linux:**
- No Application menu
- "Preferences" in Edit or Tools menu
- "Exit" in File menu

**Example: Platform-appropriate structure**

```go
menu := app.Menu.New()

// macOS gets Application menu
if runtime.GOOS == "darwin" {
    menu.AddRole(application.AppMenu)
}

// File menu
fileMenu := menu.AddSubmenu("File")
fileMenu.Add("New")
fileMenu.Add("Open")

// Preferences location varies
if runtime.GOOS == "darwin" {
    // On macOS, preferences are in Application menu (added by AppMenu role)
} else {
    // On Windows/Linux, add to Edit or Tools menu
    editMenu := menu.AddSubmenu("Edit")
    editMenu.Add("Preferences")
}
```

### Accelerator Conventions

**macOS:**
- `Cmd+` for most shortcuts
- `Cmd+,` for Preferences
- `Cmd+Q` for Quit

**Windows:**
- `Ctrl+` for most shortcuts
- `Ctrl+P` or `Ctrl+,` for Preferences
- `Alt+F4` for Exit (or `Ctrl+Q`)

**Linux:**
- Generally follows Windows conventions
- Desktop environment may override

## Best Practices

### ✅ Do

- **Call menu.Update()** after changing menu state (especially on Windows)
- **Use radio groups** for mutually exclusive options
- **Use checkboxes** for toggleable features
- **Add accelerators** to common actions
- **Group related items** with separators
- **Test on all platforms** - behaviour varies

### ❌ Don't

- **Don't forget menu.Update()** - Click handlers won't work properly
- **Don't nest too deeply** - 2-3 levels maximum
- **Don't start/end with separators** - Looks unprofessional
- **Don't use tooltips on macOS** - Not supported
- **Don't hardcode platform shortcuts** - Use `CmdOrCtrl`

## Troubleshooting

### Menu Items Not Responding

**Symptom:** Click handlers don't fire

**Cause:** Forgot to call `menu.Update()` after enabling item

**Solution:**

```go
menuItem.SetEnabled(true)
menu.Update()  // Add this!
```

### Menu Items Greyed Out

**Symptom:** Can't click menu items

**Cause:** Items are disabled

**Solution:**

```go
menuItem.SetEnabled(true)
menu.Update()
```

### Accelerators Not Working

**Symptom:** Keyboard shortcuts don't trigger menu items

**Causes:**
1. Accelerator format incorrect
2. Conflict with system shortcuts
3. Window doesn't have focus

**Solution:**

```go
// Check format
menuItem.SetAccelerator("CmdOrCtrl+S")  // ✅ Correct
menuItem.SetAccelerator("Ctrl+S")       // ❌ Wrong (macOS uses Cmd)

// Avoid conflicts
// ❌ Cmd+H (Hide Window on macOS - system shortcut)
// ✅ Cmd+Shift+H (Custom shortcut)
```

## Next Steps

- [Application Menus](/features/menus/application) - Create application menu bars
- [Context Menus](/features/menus/context) - Right-click context menus
- [System Tray Menus](/features/menus/systray) - System tray/menu bar menus
- [Menu Patterns](/guides/patterns/menus) - Common menu patterns and best practices

---

**Questions?** Ask in [Discord](https://discord.gg/JDdSxwjhGf) or check the [menu examples](https://github.com/wailsapp/wails/tree/v3-alpha/v3/examples/menu).
