Menu
Menu is a dropdown menu component with keyboard navigation and click-outside closing.
Open in Storybook
Import
import { Menu, MenuButton, MenuList, MenuItem } from "@tosui/react";
Basic usage
- Preview
- Code
<Menu>
<MenuButton>Actions</MenuButton>
<MenuList>
<MenuItem onClick={() => console.log("Edit")}>Edit</MenuItem>
<MenuItem onClick={() => console.log("Duplicate")}>Duplicate</MenuItem>
<MenuItem onClick={() => console.log("Delete")}>Delete</MenuItem>
</MenuList>
</Menu>
Button variants
MenuButton accepts all Button props.
- Preview
- Code
<Menu>
<MenuButton variant="outline">Outline Menu</MenuButton>
<MenuList>
<MenuItem>Option 1</MenuItem>
<MenuItem>Option 2</MenuItem>
</MenuList>
</Menu>
<Menu>
<MenuButton variant="ghost" colorScheme="accent">Ghost Menu</MenuButton>
<MenuList>
<MenuItem>Option 1</MenuItem>
<MenuItem>Option 2</MenuItem>
</MenuList>
</Menu>
Disabled items
- Preview
- Code
<Menu>
<MenuButton>Options</MenuButton>
<MenuList>
<MenuItem>Available</MenuItem>
<MenuItem disabled>Disabled</MenuItem>
<MenuItem>Another Option</MenuItem>
</MenuList>
</Menu>
Controlled state
Pass isOpen when application state owns the open state. Update that state from onOpen and onClose.
function ControlledMenu() {
const [isOpen, setIsOpen] = useState(false);
return (
<Menu
isOpen={isOpen}
onOpen={() => setIsOpen(true)}
onClose={() => setIsOpen(false)}
>
<MenuButton>Controlled Menu</MenuButton>
<MenuList>
<MenuItem>Option 1</MenuItem>
<MenuItem>Option 2</MenuItem>
</MenuList>
</Menu>
);
}
Common patterns
User account Menu
<Menu>
<MenuButton variant="ghost">
<Avatar size="sm" name="John Doe" />
</MenuButton>
<MenuList>
<MenuItem onClick={() => navigate("/profile")}>Profile</MenuItem>
<MenuItem onClick={() => navigate("/settings")}>Settings</MenuItem>
<MenuItem onClick={() => logout()}>Sign Out</MenuItem>
</MenuList>
</Menu>
Actions menu
<Menu>
<MenuButton size="sm" variant="ghost">
<MoreIcon />
</MenuButton>
<MenuList>
<MenuItem onClick={handleEdit}>Edit</MenuItem>
<MenuItem onClick={handleDuplicate}>Duplicate</MenuItem>
<MenuItem onClick={handleArchive}>Archive</MenuItem>
<MenuItem onClick={handleDelete}>Delete</MenuItem>
</MenuList>
</Menu>
Behavior
- Click outside closes the menu
- Escape key closes the menu
- Clicking a MenuItem closes the menu
- Menu opens below the button by default
Props reference
Menu
| Prop | Type | Default | Description |
|---|---|---|---|
| isOpen | boolean | - | Controlled open state |
| onOpen | () => void | - | Called when menu opens |
| onClose | () => void | - | Called when menu closes |
| className | string | - | Additional CSS class |
| children | ReactNode | - | Menu subcomponents |
MenuButton
MenuButton accepts all Button props.
MenuList
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | - | Additional CSS class |
| children | ReactNode | - | Menu items |
MenuItem
| Prop | Type | Default | Description |
|---|---|---|---|
| onClick | () => void | - | Click handler |
| disabled | boolean | false | Disable the item |
| className | string | - | Additional CSS class |
| children | ReactNode | - | Item content |
Accessibility
MenuButtonusesaria-haspopup="menu"andaria-expanded.MenuListusesrole="menu"and focuses its first enabled item when it opens.MenuItemusesrole="menuitem".- Up Arrow and Down Arrow move focus between enabled items.
- Escape closes the menu and returns focus to
MenuButton. - Tab closes the menu and moves focus beyond the trigger.
TypeScript
import { Menu, MenuButton, MenuList, MenuItem, type MenuProps, type MenuButtonProps, type MenuListProps, type MenuItemProps } from "@tosui/react";