pnpm dlx dreamy add menuBasic usage of Menu.
<Menu.Root positioning={{ placement: "bottom-start" }}>
<Menu.Trigger>
<Button>Open Menu</Button>
</Menu.Trigger>
<Menu.Content>
<Menu.Item icon={<IoAdd />} command="{actionKey} n">Add new</Menu.Item>
<Menu.Item icon={<LuAlarmClock />} command="{actionKey} a">Set alarm</Menu.Item>
<Menu.Item icon={<LuBattery />} command="{actionKey} b">Battery</Menu.Item>
<Menu.Item icon={<LuTrash />} command="{actionKey} d">Delete</Menu.Item>
</Menu.Content>
</Menu.Root>Use popoverProps on Menu.Root to pass behavior props to the internal Popover . Keep placement options on the root positioning prop.
Menu uses the shared Portal stack at the dropdown layer (1000). Inside a Dreamy UI overlay, its scope nests automatically and adds 1000 to the parent base; nested menus continue the same scoped behavior. Keep the portal enabled—no manual layer or z-index is needed. For a native or third-party overlay, target its non-scrolling root with popoverProps={{ portalProps: { containerRef } }}.
<Menu.Root popoverProps={{ lazyBehavior: "unmount" }}>
<Menu.Trigger>
<Button>Open Menu</Button>
</Menu.Trigger>
<Menu.Content>
<Menu.Item>Add new</Menu.Item>
<Menu.Item>Delete</Menu.Item>
</Menu.Content>
</Menu.Root>You can change where the Menu is placed by using the placement prop. Use icon prop to place an icon on the beginning or the item and comamnd to show keybinding to the item. Using {actionKey} in command will automatically replace it with the action key of the user's operating system.
<Menu.Root positioning={{ placement: "bottom-start" }}>
<Menu.Trigger>
<Button>Open Menu</Button>
</Menu.Trigger>
<Menu.Content>
<Menu.Item icon={<IoAdd />} command="{actionKey} n">Add new</Menu.Item>
<Menu.Item icon={<LuAlarmClock />} command="{actionKey} a">Set alarm</Menu.Item>
<Menu.Item icon={<LuBattery />} command="{actionKey} b">Battery</Menu.Item>
<Menu.Item icon={<LuTrash />} command="{actionKey} d">Delete</Menu.Item>
</Menu.Content>
</Menu.Root>Menu comes with 6 different sizes.
{["xs", "sm", "md", "lg", "xl", "2xl"].map((size) => (
<Menu.Root key={size} size={size}>
<Menu.Trigger>
<Button>Open Menu</Button>
</Menu.Trigger>
<Menu.Content>
<Menu.Item icon={<IoAdd />} command="{actionKey} n">Add new</Menu.Item>
<Menu.Item icon={<LuAlarmClock />} command="{actionKey} a">Set alarm</Menu.Item>
<Menu.Item icon={<LuBattery />} command="{actionKey} b">Battery</Menu.Item>
<Menu.Item icon={<LuTrash />} command="{actionKey} d">Delete</Menu.Item>
</Menu.Content>
</Menu.Root>
))}Use variant prop to set the variant of the menu.
export function VariantMenus() {
return (
<Flex
wrapped
gap={5}
>
{(
["plain", "stretched"]
).map((variant) => (
<VariantMenu key={variant} variant={variant} />
))}
</Flex>
);
}
export function VariantMenu({ variant }: { variant: string }) {
return (
<Menu.Root variant={variant as any}>
<Menu.Trigger>
<Button w={"fit-content"}>Open Menu</Button>
</Menu.Trigger>
<Menu.Content>
<Menu.Item
icon={<LuWarehouse />}
command="{actionKey} h"
as={<Link to="/" />}
>
Homepage
</Menu.Item>
<Menu.Item
icon={<IoAdd />}
command="{actionKey} n"
>
Add new
</Menu.Item>
<Menu.Item
icon={<LuAlarmClock />}
command="{actionKey} a"
>
Set alarm
</Menu.Item>
<Menu.Item
icon={<LuBattery />}
command="{actionKey} b"
>
Battery
</Menu.Item>
<Menu.Item
icon={<LuTrash />}
command="{actionKey} d"
>
Delete
</Menu.Item>
</Menu.Content>
</Menu.Root>
);
}You can use a custom link component with menu item but polymorphic props like as, asComp and asChild.
<Menu.Root positioning={{ placement: "bottom-start" }}>
<Menu.Trigger>
<Button w={"fit-content"}>Open Menu</Button>
</Menu.Trigger>
<Menu.Content>
<Menu.Item
icon={<LuWarehouse />}
command="{actionKey} h"
as={<Link to="/" />}
>
Homepage
</Menu.Item>
<Menu.Item icon={<IoAdd />} command="{actionKey} n">Add new</Menu.Item>
<Menu.Item icon={<LuAlarmClock />} command="{actionKey} a">Set alarm</Menu.Item>
<Menu.Item icon={<LuBattery />} command="{actionKey} b">Battery</Menu.Item>
<Menu.Item icon={<LuTrash />} command="{actionKey} d">Delete</Menu.Item>
</Menu.Content>
</Menu.Root>You can control the menu with isOpen, onOpen and onClose props with useControllable hook.
export function ControlledMenu() {
const { isOpen, onClose, onOpen } = useControllable();
return (
<Menu.Root
isOpen={isOpen}
onOpen={onOpen}
onClose={onClose}
>
<Text>{isOpen ? "Open" : "Closed"}</Text>
<Menu.Trigger>
<Button>Open Menu</Button>
</Menu.Trigger>
<Menu.Content>
<Menu.Item
icon={<LuWarehouse />}
command="{actionKey} h"
as={<Link to="/" />}
>
Homepage
</Menu.Item>
<Menu.Item icon={<IoAdd />} command="{actionKey} n">Add new</Menu.Item>
<Menu.Item icon={<LuAlarmClock />} command="{actionKey} a">Set alarm</Menu.Item>
<Menu.Item icon={<LuBattery />} command="{actionKey} b">Battery</Menu.Item>
<Menu.Item icon={<LuTrash />} command="{actionKey} d">Delete</Menu.Item>
</Menu.Content>
</Menu.Root>
);
}command props does not provide any logic. You can use useEventListener hook to add logic to the commands.
export function InteractiveMenu() {
const navigate = useNavigate();
useEventListener("keydown", (event) => {
if (!event[getActionKeyCode()]) return; // Making sure the ctrl/cmd key is pressed
switch (event.key) {
case "h": {
event.preventDefault();
navigate("/");
alert("Homepage");
break;
}
case "n": {
event.preventDefault();
alert("New");
break;
}
case "a": {
event.preventDefault();
alert("Alarm");
break;
}
case "b": {
event.preventDefault();
alert("Battery");
break;
}
case "d": {
event.preventDefault();
alert("Delete");
break;
}
}
});
return (
<Menu.Root>
<Menu.Trigger>
<Button>Open Menu</Button>
</Menu.Trigger>
<Menu.Content>
<Menu.Item
icon={<LuWarehouse />}
command="{actionKey} h"
as={<Link to="/" />}
>
Homepage
</Menu.Item>
<Menu.Item
icon={<IoAdd />}
command="{actionKey} n"
>
Add new
</Menu.Item>
<Menu.Item
icon={<LuAlarmClock />}
command="{actionKey} a"
>
Set alarm
</Menu.Item>
<Menu.Item
icon={<LuBattery />}
command="{actionKey} b"
>
Battery
</Menu.Item>
<Menu.Item
icon={<LuTrash />}
command="{actionKey} d"
>
Delete
</Menu.Item>
</Menu.Content>
</Menu.Root>
);
}You can add right content to the item with rightContent prop.
<Menu.Root>
<Menu.Trigger>
<Button>Open Menu</Button>
</Menu.Trigger>
<Menu.Content>
<Menu.Item rightContent={<Icon as={LuFileWarning} color="warning" boxSize={4} />}>
Select File
</Menu.Item>
</Menu.Content>
</Menu.Root>Use Menu.TriggerItem to create sub-menus. Hover over the item or press ArrowRight to open the sub-menu, and ArrowLeft or Escape to close it and return focus to the parent.
<Menu.Root>
<Menu.Trigger>
<Button>Open Menu</Button>
</Menu.Trigger>
<Menu.Content>
<Menu.Item icon={<IoAdd />}>Add new</Menu.Item>
<Menu.Item icon={<LuAlarmClock />}>Set alarm</Menu.Item>
{/* TriggerItem opens a sub-menu to the right */}
<Menu.TriggerItem label="More options">
<Menu.Content>
<Menu.Item icon={<LuBattery />}>Battery</Menu.Item>
<Menu.Item icon={<LuTrash />}>Delete</Menu.Item>
{/* Sub-menus can be nested arbitrarily deep */}
<Menu.TriggerItem label="Advanced">
<Menu.Content>
<Menu.Item>Export data</Menu.Item>
<Menu.Item>Import data</Menu.Item>
<Menu.Item>Reset settings</Menu.Item>
</Menu.Content>
</Menu.TriggerItem>
</Menu.Content>
</Menu.TriggerItem>
</Menu.Content>
</Menu.Root>You can control the opening direction with the positioning prop:
<Menu.TriggerItem label="More options" positioning={{ placement: "left-start" }}>
<Menu.Content>
<Menu.Item>Sub item</Menu.Item>
</Menu.Content>
</Menu.TriggerItem>