DropdownMenu
A click-triggered menu with keyboard navigation, grouped items, checkable options, and nested submenus.
import { Button } from "@glint/components/button.slint";import { DropdownMenu } from "@glint/components/dropdown-menu.slint";import { MenuEntry } from "@glint/components/menu-entry.slint";import { Tokens } from "@glint/theme/tokens.slint";import { IconSet } from "@lucide";
export component Demo inherits Window { width: 560px; height: 320px; background: Tokens.color-background;
in-out property <string> last-action: "None";
VerticalLayout { alignment: center; spacing: 16px; padding: 24px;
HorizontalLayout { alignment: center;
menu := DropdownMenu { items: [ { label: "New File", icon: IconSet.FilePlus, shortcut: "Ctrl+N" }, { label: "Open…", icon: IconSet.FolderOpen, shortcut: "Ctrl+O" }, { label: "Save", icon: IconSet.Save, shortcut: "Ctrl+S" }, { label: "Preferences", icon: IconSet.Settings, separator-before: true, children: [ { label: "Keyboard Shortcuts", shortcut: "Ctrl+K" }, { label: "Settings", shortcut: "Ctrl+," }, ] }, ]; selected(row, child) => { if (child >= 0) { root.last-action = "Selected submenu item " + child + " in row " + row; } else { root.last-action = "Selected row " + row; } }
Button { text: "File"; haspopup: true; clicked => { menu.show(); } } } }
Text { text: "Last action: " + root.last-action; color: Tokens.color-muted-foreground; font-size: Tokens.typography-body-sm-size; horizontal-alignment: center; } }}Usage
import { Button } from "@glint/components/button.slint";import { DropdownMenu } from "@glint/components/dropdown-menu.slint";
export component AppWindow inherits Window { VerticalLayout { alignment: center;
menu := DropdownMenu { items: [ { label: "Profile" }, { label: "Billing" }, { label: "Sign out", separator-before: true } ]; selected(row, child) => { // handle choice }
Button { text: "My Account"; haspopup: true; clicked => { menu.show(); } } } }}DropdownMenu wraps a trigger element in its @children slot (typically a Button).
Clicking the trigger opens a popup card powered by MenuPanel presenting the items model.
The menu manages keyboard navigation, submenus, checkable items, and radio groups.
The model is two-way: activating a checkbox or radio row immediately updates the checked property
in the items array before the selected(row, child) callback fires.
Examples
Submenu placement and alignment
When a menu row carries children, hovering it or pressing → opens a submenu panel.
Use submenu-side (PanelSide.right, PanelSide.left) and submenu-align (PanelAlign.start, PanelAlign.center, PanelAlign.end) to control where the nested panel appears relative to the parent item.
import { Button } from "@glint/components/button.slint";import { DropdownMenu } from "@glint/components/dropdown-menu.slint";import { PanelSide, PanelAlign } from "@glint/components/panel.slint";import { Tokens } from "@glint/theme/tokens.slint";import { IconSet } from "@lucide";
export component Demo inherits Window { width: 560px; height: 320px; background: Tokens.color-background;
VerticalLayout { alignment: center; padding: 24px;
HorizontalLayout { alignment: center;
menu := DropdownMenu { submenu-side: PanelSide.left; submenu-align: PanelAlign.start; min-content-width: 220px; items: [ { label: "Cut", icon: IconSet.Scissors, shortcut: "Ctrl+X" }, { label: "Copy", icon: IconSet.Copy, shortcut: "Ctrl+C" }, { label: "Paste", icon: IconSet.Clipboard, shortcut: "Ctrl+V" }, { label: "More Options", separator-before: true, children: [ { label: "Find and Replace" }, { label: "Format Document" }, ] } ];
Button { text: "Edit (Opens Left)"; haspopup: true; clicked => { menu.show(); } } } } }}Checkable and radio rows
Set checkable: true on an item for a toggleable checkbox row. Adding a non-empty radio-group string links the item into a mutually exclusive group where picking one item clears other items in the same group on the same frame.
import { Button } from "@glint/components/button.slint";import { DropdownMenu } from "@glint/components/dropdown-menu.slint";import { MenuEntry } from "@glint/components/menu-entry.slint";import { Tokens } from "@glint/theme/tokens.slint";
export component Demo inherits Window { width: 560px; height: 320px; background: Tokens.color-background;
in-out property <[MenuEntry]> view-items: [ { label: "Show Bookmarks", checkable: true, checked: true }, { label: "Show Full URLs", checkable: true, checked: false }, { label: "Compact Density", separator-before: true, heading-before: "Density", checkable: true, checked: true, radio-group: "density" }, { label: "Comfortable Density", checkable: true, checked: false, radio-group: "density" }, ];
VerticalLayout { alignment: center; padding: 24px;
HorizontalLayout { alignment: center;
menu := DropdownMenu { items <=> root.view-items;
Button { text: "View Options"; haspopup: true; clicked => { menu.show(); } } } } }}Group headings and destructive items
Following the grouped rows model, use separator-before: true and heading-before: "..." to group related options without changing item indexing. Set tone: MenuTone.destructive on actions that delete or destroy data.
import { Button } from "@glint/components/button.slint";import { DropdownMenu } from "@glint/components/dropdown-menu.slint";import { MenuTone } from "@glint/components/menu-entry.slint";import { Tokens } from "@glint/theme/tokens.slint";import { IconSet } from "@lucide";
export component Demo inherits Window { width: 560px; height: 320px; background: Tokens.color-background;
VerticalLayout { alignment: center; padding: 24px;
HorizontalLayout { alignment: center;
menu := DropdownMenu { items: [ { label: "Edit Item", icon: IconSet.Pencil }, { label: "Duplicate", icon: IconSet.Copy }, { label: "Archive", separator-before: true, heading-before: "Danger Zone", icon: IconSet.Archive }, { label: "Delete", icon: IconSet.Trash2, tone: MenuTone.destructive }, ];
Button { text: "Manage Item"; haspopup: true; clicked => { menu.show(); } } } } }}API Reference
Properties
| Property | Type | Default | Description |
|---|---|---|---|
items | in-out [MenuEntry] | no default | Menu rows shown when the trigger is clicked. in-out because activating a checkbox or radio row writes its new checked back here. |
min-content-width | in length | 200px | The floor under the popup panel's width; the panel grows past it to fit its widest row, icon and shortcut hint included. |
submenu-side | in PanelSide | PanelSide.right | Where a row's submenu panel opens: which side of the menu, and how it lines up against the row it hangs off. A submenu opens beside its row, so top and bottom read as right. |
submenu-align | in PanelAlign | PanelAlign.start | |
highlighted-index | in-out int | 0 | Row highlighted by ↑/↓ inside the open popup; consumers rarely set this. |
is-open | out bool | no default | True while the menu is on screen; mirrors PopupWindow.is-open. |
Callbacks
| Callback | Description |
|---|---|
selected(int , int) | Fired with the chosen row's index, and with the index of the submenu leaf that was taken or -1 when the row itself was; the popup auto-closes. |
Functions
| Function | Description |
|---|---|
show() | Opens the menu with the highlight back at the top. Mirrors PopupWindow.show() — wire it to a consumer-owned trigger so the menu opens from the keyboard as well as from a click. |
close() | Closes the menu. Mirrors PopupWindow.close(). |
Enums
| Enum | Values |
|---|---|
PanelSide | top, right, bottom, left |
PanelAlign | start, center, end |
Accessibility
- Role and structure. The open dropdown menu presents an accessible
listcontaininglist-itemrows withaccessible-item-countand 1-basedaccessible-item-indexvalues. - Checkable and expandable states. Checkbox and radio rows announce their
checkedstate, and rows with submenus announce themselves asexpandable. - Shortcut hints. Shortcut strings ride as
accessible-descriptionon the row rather than creating extra text nodes. - Group headings. Group headers and separator lines are presented as non-actionable labels and never receive keyboard focus.
- Keyboard navigation. ↑/↓ navigate the list, Home/End jump to the first and last options, → opens a submenu, ← closes the active submenu, Enter or Space activates the highlighted option, and Escape closes the menu (or the open submenu first).