Skip to content
Glint UI

MenuPanel

The shared menu surface component that renders menu items, group headers, and nested submenu cards.

Usage

import { MenuPanel } from "@glint/components/menu-panel.slint";
import { MenuEntry } from "@glint/components/menu-entry.slint";
export component AppWindow inherits Window {
VerticalLayout {
alignment: center;
MenuPanel {
items: [
{ label: "Profile" },
{ label: "Settings" },
];
selected(row, child) => {
// handle pick
}
}
}
}

MenuPanel is the core surface component behind DropdownMenu, ContextMenu, and Menubar. It renders the styled card, item list, group headers, checkmarks, and nested submenu cards in a single coordinate system.

The model is defined by MenuEntry, MenuSubEntry, and MenuTone. Grouping headers and dividers follow the unified grouped rows pattern established by Select.

  • label: string — Display text for the menu item.
  • icon: LucideIcon — Leading icon rendered from @lucide.
  • shortcut: string — Trailing keyboard shortcut description (rendered with Kbd).
  • separator-before: bool — Draws a hairline separator above this row.
  • heading-before: string — Renders a non-selectable category heading above this row.
  • tone: MenuTone — Visual tone (MenuTone.default or MenuTone.destructive).
  • checkable: bool — When true, renders this item as a toggleable checkbox row.
  • checked: bool — Boolean check state read by the panel. Handle checkbox-toggled or radio-picked and write the new state back to items; MenuRadioSet can clear sibling radio rows after a radio pick.
  • radio-group: string — Associates checkable items into a mutually exclusive group.
  • children: [MenuSubEntry] — Array of sub-entries; presence of children turns this row into an expandable submenu opener.

MenuSubEntry carries command fields (label, icon, shortcut, separator-before, heading-before, tone) for submenu leaves.

Examples

Use submenu-side (PanelSide.right, PanelSide.left) and submenu-align (PanelAlign.start, PanelAlign.center, PanelAlign.end) to position nested submenu cards.

Radio synchronization with MenuRadioSet

MenuRadioSet is an invisible helper that listens to radio selection events and reactively clears sibling radio rows in the same radio-group.

Destructive actions and item tones

Use tone: MenuTone.destructive on actions that delete or irreversibly alter user data. Destructive rows render with highlight styling that reinforces caution.

API Reference

Properties

PropertyTypeDefaultDescription
itemsin [MenuEntry]no defaultThe menu's rows. Read-only here: the panel reports what was picked and the menu around it owns the write-back, because a Menubar's rows live two models deep and a binding cannot be aliased into that (ADR-0020).
min-content-widthin length200pxThe floor under the card's width. The card grows past it to fit its widest row, gutter, shortcut hint and chevron included.
highlighted-indexin-out int0Row the keyboard stands on.
open-submenu-indexin-out int-1Row whose submenu is open, or -1 for none.
submenu-highlightin-out int0Row the keyboard stands on inside that submenu.
submenu-sidein PanelSidePanelSide.rightWhich side of the menu a submenu panel opens on, and how it lines up against the row it hangs off. A submenu opens *beside* its row, so top and bottom have nothing to mean here and read as right. The panel never rises above the card's top edge, whatever the alignment asks for: the popup's box starts there.
submenu-alignin PanelAlignPanelAlign.start
submenu-openout boolno defaultTrue while a submenu panel is on screen.
panel-widthout lengthno defaultThe card's own width: never below the floor, otherwise its widest row.
card-xout lengthno defaultA submenu opening to the left pushes the card right inside the popup's box; the menu around this panel subtracts this from where it opens, so the card itself stays anchored where it was asked for.
total-widthout lengthno defaultThe whole popup's box — the card, plus the submenu wherever it landed.
total-heightout lengthno default

Callbacks

CallbackDescription
selected(int , int)Fired with the row's index and, when the row taken was a submenu leaf, that leaf's index inside the row's children — otherwise -1.
checkbox-toggled(int)Fired when a checkbox row is taken, just before selected, so the consumer can flip it and selected can observe a settled model.
radio-picked(string , int)Fired when a radio row is picked, naming its group and its index. The menu around this panel owns the clearing of the group's other rows, because the panel dies with the popup the pick closes (ADR-0020).

Functions

FunctionDescription
open-sub-at(index: int, y: length, h: length)Open the submenu of the row at index, whose top edge sits y below the card's and which is h tall. The pointer path calls this: the row that was hovered knows its own geometry.
open-submenu()Open the highlighted row's submenu, if it has one. The keyboard path, which has only the geometry the highlighted row last reported.
can-open-submenu() -> boolTrue when the highlighted row has a submenu to open.
close-submenu()Close the open submenu, leaving the keyboard on the row that opened it.
activate-highlighted()Take the highlighted row, or the highlighted leaf of the open submenu.
handle-key(text: string) -> boolThe keys a menu's insides own: the highlight, the ends of the list and the activation. Esc and the ←/→ walk stay with the menu around this panel, because what they mean depends on what the menu is — a menubar walks on to its neighbour where a dropdown has nowhere to go. Returns true when the key was the panel's.

Enums

EnumValues
PanelSidetop, right, bottom, left
PanelAlignstart, center, end

An invisible bookkeeping helper that maintains single-choice selection across items with matching radio-group identifiers.

Properties

PropertyTypeDefaultDescription
itemsin-out [MenuEntry]no defaultThe same rows the menu shows, written through.
groupin stringno defaultThe group that was picked, and the row of it that took the pick.
picked-rowin intno default
generationin intno defaultBumped by the menu on every pick; a counter rather than a flag, so two picks in the same group still read as two events.

Accessibility

  • List container. MenuPanel presents an accessible list role carrying accessible-item-count matching the number of actionable rows.
  • Row roles. Each item is exposed as a list-item carrying its index, label, checkable state, and shortcut descriptions.
  • Keyboard navigation. handle-key(string) processes arrow keys, Home, End, Space, and Enter, dispatching highlight moves and selection activations.
  • Group headings. Group headers rendered by GroupOpener are treated as non-focusable separator labels.