Menu API
Overview
Section titled “Overview”The Menu API provides methods to create and manage application menus, context menus, and system tray menus.
Menu Types:
- Application Menus - Top menu bar (File, Edit, etc.)
- Context Menus - Right-click menus
- System Tray Menus - Menus in the system tray/notification area
Creating Menus
Section titled “Creating Menus”NewMenu()
Section titled “NewMenu()”Creates a new menu.
func (a *App) NewMenu() *MenuExample:
menu := app.NewMenu()Menu Methods
Section titled “Menu Methods”Adds a menu item to the menu.
func (m *Menu) Add(label string) *MenuItemParameters:
label- The text displayed for the menu item
Returns: The created menu item
Example:
item := menu.Add("Open File")item.OnClick(func(ctx *application.Context) { // Handle click})AddSubmenu()
Section titled “AddSubmenu()”Adds a submenu to the menu.
func (m *Menu) AddSubmenu(label string) *MenuParameters:
label- The submenu label
Returns: The created submenu
Example:
fileMenu := menu.AddSubmenu("File")fileMenu.Add("New")fileMenu.Add("Open")fileMenu.Add("Save")AddSeparator()
Section titled “AddSeparator()”Adds a visual separator line between menu items.
func (m *Menu) AddSeparator()Example:
menu.Add("Copy")menu.Add("Paste")menu.AddSeparator()menu.Add("Select All")Best practice: Use separators to group related menu items.
AddCheckbox()
Section titled “AddCheckbox()”Adds a checkable menu item.
func (m *Menu) AddCheckbox(label string, checked bool) *MenuItemParameters:
label- The checkbox labelchecked- Initial checked state
Example:
darkMode := menu.AddCheckbox("Dark Mode", false)darkMode.OnClick(func(ctx *application.Context) { isChecked := darkMode.Checked() // Toggle dark mode})AddRadio()
Section titled “AddRadio()”Adds a radio menu item (mutually exclusive group).
func (m *Menu) AddRadio(label string, checked bool) *MenuItemParameters:
label- The radio button labelchecked- Initial checked state
Example:
// Create radio group for view modesviewMenu := menu.AddSubmenu("View")listView := viewMenu.AddRadio("List View", true)gridView := viewMenu.AddRadio("Grid View", false)treeView := viewMenu.AddRadio("Tree View", false)
listView.OnClick(func(ctx *application.Context) { setViewMode("list")})gridView.OnClick(func(ctx *application.Context) { setViewMode("grid")})Update()
Section titled “Update()”Updates the menu to reflect any changes made to menu items.
func (m *Menu) Update()Example:
item.SetEnabled(false)menu.Update() // Must call to apply changesImportant: Always call Update() after modifying menu item properties.
Menu Item Methods
Section titled “Menu Item Methods”OnClick()
Section titled “OnClick()”Registers a click handler for the menu item.
func (mi *MenuItem) OnClick(callback func(ctx *application.Context)) *MenuItemParameters:
callback- Function called when item is clicked
Returns: The menu item (for chaining)
Example:
item.OnClick(func(ctx *application.Context) { fmt.Println("Menu item clicked") app.Logger.Info("User clicked menu item")})SetLabel()
Section titled “SetLabel()”Changes the menu item’s label.
func (mi *MenuItem) SetLabel(label string) *MenuItemExample:
item.SetLabel("Save As...")menu.Update()SetEnabled()
Section titled “SetEnabled()”Enables or disables the menu item.
func (mi *MenuItem) SetEnabled(enabled bool) *MenuItemExample:
// Disable save when no document is opensaveItem.SetEnabled(hasOpenDocument)menu.Update()Common pattern:
// Update menu state based on application statefunc updateMenuState() { saveItem.SetEnabled(hasUnsavedChanges) undoItem.SetEnabled(canUndo) redoItem.SetEnabled(canRedo) menu.Update()}SetChecked()
Section titled “SetChecked()”Sets the checked state for checkbox/radio menu items.
func (mi *MenuItem) SetChecked(checked bool) *MenuItemExample:
darkModeItem.SetChecked(isDarkModeEnabled)menu.Update()Checked()
Section titled “Checked()”Returns the current checked state.
func (mi *MenuItem) Checked() boolExample:
if darkModeItem.Checked() { // Dark mode is enabled}SetAccelerator()
Section titled “SetAccelerator()”Sets a keyboard shortcut for the menu item.
func (mi *MenuItem) SetAccelerator(accelerator string) *MenuItemParameters:
accelerator- Keyboard shortcut (e.g., “Ctrl+S”, “Cmd+Q”)
Accelerator format:
- Modifiers:
Ctrl,Cmd,Alt,Shift - Keys:
A-Z,0-9,F1-F12,Enter,Backspace, etc. - Platform: Use
Cmdon macOS,Ctrlon Windows/Linux
Example:
saveItem.SetAccelerator("Ctrl+S")quitItem.SetAccelerator("Ctrl+Q")newItem.SetAccelerator("Ctrl+N")Platform-aware example:
import "runtime"
var quitShortcut stringif runtime.GOOS == "darwin" { quitShortcut = "Cmd+Q"} else { quitShortcut = "Ctrl+Q"}quitItem.SetAccelerator(quitShortcut)SetTooltip()
Section titled “SetTooltip()”Sets a tooltip that appears when hovering over the menu item.
func (mi *MenuItem) SetTooltip(tooltip string) *MenuItemExample:
item.SetTooltip("Opens a file from disk")SetHidden()
Section titled “SetHidden()”Shows or hides the menu item.
func (mi *MenuItem) SetHidden(hidden bool) *MenuItemExample:
// Hide debug menu in productiondebugItem.SetHidden(!isDevelopment)menu.Update()Application Menu
Section titled “Application Menu”app.Menu.Set()
Section titled “app.Menu.Set()”Sets the application’s main menu bar.
func (mm *MenuManager) Set(menu *Menu)Example:
menu := app.NewMenu()
// File menufileMenu := menu.AddSubmenu("File")fileMenu.Add("New").SetAccelerator("Ctrl+N").OnClick(newFile)fileMenu.Add("Open").SetAccelerator("Ctrl+O").OnClick(openFile)fileMenu.Add("Save").SetAccelerator("Ctrl+S").OnClick(saveFile)fileMenu.AddSeparator()fileMenu.Add("Exit").SetAccelerator("Ctrl+Q").OnClick(func(ctx *application.Context) { app.Quit()})
// Edit menueditMenu := menu.AddSubmenu("Edit")editMenu.Add("Undo").SetAccelerator("Ctrl+Z").OnClick(undo)editMenu.Add("Redo").SetAccelerator("Ctrl+Y").OnClick(redo)editMenu.AddSeparator()editMenu.Add("Cut").SetAccelerator("Ctrl+X").OnClick(cut)editMenu.Add("Copy").SetAccelerator("Ctrl+C").OnClick(copy)editMenu.Add("Paste").SetAccelerator("Ctrl+V").OnClick(paste)
app.Menu.Set(menu)Platform notes:
- macOS: Menu appears in the top menu bar
- Windows/Linux: Menu appears in the window title bar
- macOS: Automatically adds application menu with app name
Context Menus
Section titled “Context Menus”app.ContextMenu.Add()
Section titled “app.ContextMenu.Add()”Registers a context menu (right-click menu) with a specific name.
func (cm *ContextMenuManager) Add(name string, menu *Menu)Parameters:
name- Unique identifier for the context menumenu- The menu to show
Go:
// Create context menucontextMenu := app.NewMenu()contextMenu.Add("Cut").OnClick(cut)contextMenu.Add("Copy").OnClick(copy)contextMenu.Add("Paste").OnClick(paste)contextMenu.AddSeparator()contextMenu.Add("Select All").OnClick(selectAll)
// Register itapp.RegisterContextMenu("editor", contextMenu)HTML:
<!-- Trigger context menu on right-click --><div data-wails-context-menu="editor"> Right-click here for context menu</div>Dynamic context menus:
// Update context menu based on selectionfunc updateContextMenu() { contextMenu := app.NewMenu()
if hasSelection { contextMenu.Add("Cut").OnClick(cut) contextMenu.Add("Copy").OnClick(copy) }
contextMenu.Add("Paste").SetEnabled(hasClipboardContent).OnClick(paste)
app.RegisterContextMenu("editor", contextMenu)}System Tray Menu
Section titled “System Tray Menu”app.SystemTray.New()
Section titled “app.SystemTray.New()”Creates a new system tray icon.
func (sm *SystemTrayManager) New() *SystemTrayExample:
tray := app.SystemTray.New()SetIcon()
Section titled “SetIcon()”Sets the system tray icon.
func (st *SystemTray) SetIcon(icon []byte) *SystemTrayExample:
iconData, _ := os.ReadFile("icon.png")tray.SetIcon(iconData)SetMenu()
Section titled “SetMenu()”Sets the menu for the system tray.
func (st *SystemTray) SetMenu(menu *Menu) *SystemTrayExample:
trayMenu := app.NewMenu()trayMenu.Add("Show Window").OnClick(func(ctx *application.Context) { window.Show() window.SetFocus()})trayMenu.Add("Settings").OnClick(openSettings)trayMenu.AddSeparator()trayMenu.Add("Quit").OnClick(func(ctx *application.Context) { app.Quit()})
tray.SetMenu(trayMenu)SetTooltip()
Section titled “SetTooltip()”Sets the tooltip shown when hovering over the tray icon.
func (st *SystemTray) SetTooltip(tooltip string) *SystemTrayExample:
tray.SetTooltip("My Application - Running")OnClick()
Section titled “OnClick()”Handles left-click on the tray icon.
func (st *SystemTray) OnClick(callback func()) *SystemTrayExample:
tray.OnClick(func() { if window.IsVisible() { window.Hide() } else { window.Show() window.SetFocus() }})Complete Examples
Section titled “Complete Examples”Standard Application Menu
Section titled “Standard Application Menu”package main
import ( "github.com/wailsapp/wails/v3/pkg/application")
func createMenu(app *application.Application) *application.Menu { menu := app.NewMenu()
// File menu fileMenu := menu.AddSubmenu("File") fileMenu.Add("New"). SetAccelerator("Ctrl+N"). OnClick(func(ctx *application.Context) { // Create new document }) fileMenu.Add("Open"). SetAccelerator("Ctrl+O"). OnClick(func(ctx *application.Context) { // Open file dialog }) fileMenu.Add("Save"). SetAccelerator("Ctrl+S"). OnClick(func(ctx *application.Context) { // Save document }) fileMenu.AddSeparator() fileMenu.Add("Exit").OnClick(func(ctx *application.Context) { app.Quit() })
// Edit menu editMenu := menu.AddSubmenu("Edit") editMenu.Add("Undo").SetAccelerator("Ctrl+Z") editMenu.Add("Redo").SetAccelerator("Ctrl+Y") editMenu.AddSeparator() editMenu.Add("Cut").SetAccelerator("Ctrl+X") editMenu.Add("Copy").SetAccelerator("Ctrl+C") editMenu.Add("Paste").SetAccelerator("Ctrl+V")
// View menu viewMenu := menu.AddSubmenu("View") darkMode := viewMenu.AddCheckbox("Dark Mode", false) darkMode.OnClick(func(ctx *application.Context) { // Toggle dark mode isChecked := darkMode.Checked() app.Logger.Info("Dark mode", "enabled", isChecked) }) viewMenu.AddSeparator() viewMenu.AddRadio("List View", true) viewMenu.AddRadio("Grid View", false) viewMenu.AddRadio("Detail View", false)
// Help menu helpMenu := menu.AddSubmenu("Help") helpMenu.Add("Documentation").OnClick(func(ctx *application.Context) { // Open docs }) helpMenu.Add("About").OnClick(func(ctx *application.Context) { // Show about dialog })
return menu}
func main() { app := application.New(application.Options{ Name: "Menu Demo", })
menu := createMenu(app) app.Menu.Set(menu)
window := app.Window.New() window.Show()
app.Run()}System Tray Application
Section titled “System Tray Application”func setupSystemTray(app *application.Application, window *application.Window) { // Create system tray tray := app.SystemTray.New()
// Set icon iconData, _ := os.ReadFile("icon.png") tray.SetIcon(iconData) tray.SetTooltip("My App - Running")
// Handle left-click on tray icon tray.OnClick(func() { if window.IsVisible() { window.Hide() } else { window.Show() window.SetFocus() } })
// Create tray menu trayMenu := app.NewMenu()
showItem := trayMenu.Add("Show Window") showItem.OnClick(func(ctx *application.Context) { window.Show() window.SetFocus() })
trayMenu.AddSeparator()
trayMenu.Add("Settings").OnClick(func(ctx *application.Context) { // Open settings window })
trayMenu.Add("About").OnClick(func(ctx *application.Context) { // Show about dialog })
trayMenu.AddSeparator()
trayMenu.Add("Quit").OnClick(func(ctx *application.Context) { app.Quit() })
tray.SetMenu(trayMenu)}Dynamic Menu Updates
Section titled “Dynamic Menu Updates”type Editor struct { app *application.Application menu *application.Menu undoItem *application.MenuItem redoItem *application.MenuItem saveItem *application.MenuItem undoStack []string redoStack []string hasChanges bool}
func (e *Editor) createMenu() { e.menu = e.app.NewMenu()
fileMenu := e.menu.AddSubmenu("File") e.saveItem = fileMenu.Add("Save").SetAccelerator("Ctrl+S") e.saveItem.OnClick(func(ctx *application.Context) { e.save() })
editMenu := e.menu.AddSubmenu("Edit") e.undoItem = editMenu.Add("Undo").SetAccelerator("Ctrl+Z") e.undoItem.OnClick(func(ctx *application.Context) { e.undo() })
e.redoItem = editMenu.Add("Redo").SetAccelerator("Ctrl+Y") e.redoItem.OnClick(func(ctx *application.Context) { e.redo() })
e.updateMenuState() e.app.Menu.Set(e.menu)}
func (e *Editor) updateMenuState() { // Update menu items based on current state e.saveItem.SetEnabled(e.hasChanges) e.undoItem.SetEnabled(len(e.undoStack) > 0) e.redoItem.SetEnabled(len(e.redoStack) > 0) e.menu.Update()}
func (e *Editor) onChange() { e.hasChanges = true e.updateMenuState()}
func (e *Editor) save() { // Save logic e.hasChanges = false e.updateMenuState()}Best Practices
Section titled “Best Practices”- Use standard accelerators - Follow platform conventions (Ctrl+C for copy, etc.)
- Call Update() after changes - Menu won’t reflect changes otherwise
- Group related items - Use separators to organize menu items
- Disable unavailable actions - Don’t hide, disable with SetEnabled(false)
- Use clear labels - Be concise and descriptive
- Follow platform conventions - macOS vs Windows/Linux menu patterns
❌ Don’t
Section titled “❌ Don’t”- Don’t forget Update() - Most common mistake
- Don’t nest too deeply - Keep menus 2-3 levels maximum
- Don’t use ambiguous labels - “Process” vs “Process Document”
- Don’t overcomplicate - Keep menus simple and focused
- Don’t mix metaphors - Consistent naming and organization
Platform-Specific Notes
Section titled “Platform-Specific Notes”- Application menu automatically added with app name
- Use
Cmdinstead ofCtrlfor accelerators - “About”, “Preferences”, and “Quit” in application menu by default
Windows/Linux
Section titled “Windows/Linux”- No automatic application menu
- Use
Ctrlfor accelerators - “Exit” typically in File menu