Component reference
Menu
A list of actions in a popover, with keyboard navigation and nested submenus.
Preview
Groups and keyboard operation
Arrow keys and Home/End move the focus, typing text jumps to an item; Escape returns to the trigger button. All actions only update the local receipt.
render() {
const feedback = signal("No action selected yet.");
return (
<Stack align="start">
<Menu
id="story-actions"
trigger={(bindings) => (
<Button variant="outline" {...bindings}>
Actions
<Icon name="chevron-down" />
</Button>
)}
items={[
{
type: "group",
id: "file",
label: "File",
items: [
{
id: "open",
label: "Open",
icon: <Icon name="file" />,
shortcut: "↵",
onSelect: () => {
feedback.value = "Selected Open.";
},
},
{
id: "copy",
label: "Copy",
icon: <Icon name="copy" />,
onSelect: () => {
feedback.value = "Selected Copy.";
},
},
{ id: "export", label: "Export (unavailable)", disabled: true, onSelect: () => {} },
],
},
{ type: "separator", id: "divider" },
{
id: "remove",
label: "Remove",
danger: true,
onSelect: () => {
feedback.value = "Selected Remove sample.";
},
},
]}
/>
<Note role="status">{feedback}</Note>
</Stack>
);
}Installation
bun add meowtail
Usage
import { Menu } from "meowtail";Examples
The other stories that show this component.
Nesting and touch
Hover or click to open a submenu; the right-arrow direction enters it, the left-arrow direction goes back, and Escape closes one level at a time.
render() {
const feedback = signal("No format selected yet.");
return (
<Stack align="start">
<Menu
id="nested-actions"
trigger={(bindings) => (
<Button variant="outline" {...bindings}>
Format actions
</Button>
)}
items={[
{
id: "export",
label: "Export format",
children: [
{
id: "markdown",
label: "Markdown",
onSelect: () => {
feedback.value = "Selected Markdown.";
},
},
{ id: "disabled", label: "PDF (unavailable)", disabled: true, onSelect: () => {} },
{
id: "more",
label: "More formats",
children: [
{
id: "text",
label: "Plain text",
onSelect: () => {
feedback.value = "Selected Plain text.";
},
},
],
},
],
},
{
id: "locked",
label: "Unavailable submenu",
disabled: true,
children: [{ id: "locked-child", label: "Unreachable item", onSelect: () => {} }],
},
{
id: "copy",
label: "Copy",
onSelect: () => {
feedback.value = "Selected Copy.";
},
},
]}
/>
<Note role="status">{feedback}</Note>
</Stack>
);
}API reference
Props
| Prop | Type | Default |
|---|---|---|
idRequired | string | – |
triggerRequired | (bindings: MenuTriggerBindings) => Child | – |
itemsRequired | readonly MenuEntry[] | – |
open | MaybeSignal<boolean> | – |
onOpenChange | (open: boolean) => void | – |
align | "start" | "end" | – |
anchorRectPosition against a point such as a right-click, instead of the trigger box. | () => DOMRect | – |
anchorElementElement whose layout defines a custom rect; observed by the same autoUpdate. | () => HTMLElement | null | – |
placement | "bottom" | "right" | "bottom" |
closeAncestors | () => void | – |
Also takes className, children and the attributes and events of its root element.
DOM parts
rootitemiconlabelshortcutgroupheadingseparatorbranch
Registry
The component is registered as menu in the meowtail component registry.