Menu
Menu is a vertical navigation menu, such as an app or docs sidebar: rows with icons, submenus that expand in place, titled groups and dividers, with one selected row. It renders a <nav> landmark; give it an aria-label. For a menu that pops up from a button, use Dropdown.
Demos
Basic
Options are a tree: rows { key, label, icon? }, submenus (rows with children), groups (type: "group") and dividers (type: "divider"). The selected row’s submenus start open, and a submenu holding it is marked too.
Links and routers
A row with href is a link with aria-current="page" while selected. renderLink renders those rows with your router’s link component: spread the props it receives and keep its onClick, which selects the row.
Collapsed
collapsed shrinks the menu to collapsedWidth and shows only icons (a label’s first letter when a row has none). A tooltip names each row, and a submenu opens its rows in a popup beside the menu.
Accordion
With accordion, opening a submenu closes the open submenus next to it.
Controlled expanded keys
expandedKeys and onUpdateExpandedKeys control which submenus are open.
Indent
rootIndent is the top level’s start padding and indent is added per level. By default the indent is the icon’s width plus its gap, so a child’s label lines up with its parent’s.
Extra and disabled
extra puts trailing content, such as a count, at the end of a row. disabled rows can’t be focused or chosen.
Accessibility
The menu follows the disclosure navigation pattern, not role="menu": menu roles are for application menus of commands and switch screen readers into a mode where links and Tab stop working as usual. Here every row is a plain link or button in the Tab order, a submenu is a button with aria-expanded and aria-controls, a group is a list labelled by its title, and the selected row has aria-current (page on links, true on buttons).
Keyboard
Tab moves through the rows. Enter (and Space on buttons) chooses a row or opens a submenu. As a shortcut, ↑ / ↓ move between visible rows, Home / End jump to the ends, → opens a submenu or enters an open one, and ← closes it or goes back to the submenu a row is in.
Props
| Name | Type | Default | Description | Version |
|---|---|---|---|---|
options | MenuMixedOption[] | required | Rows, submenus, groups and dividers; see below. | Unreleased |
value / defaultValue | MenuKey | null | undefined / null | Controlled / initial selected key. | Unreleased |
onUpdateValue | (key, option) => void | undefined | Called when a row without children is chosen. | Unreleased |
expandedKeys / defaultExpandedKeys | MenuKey[] | undefined | Controlled / initial open submenus. By default the selected row’s ancestors start open. | Unreleased |
defaultExpandAll | boolean | false | Open every submenu at first. | Unreleased |
onUpdateExpandedKeys | (keys: MenuKey[]) => void | undefined | Called when a submenu opens or closes. | Unreleased |
accordion | boolean | false | Opening a submenu closes its open siblings. | Unreleased |
indent / rootIndent | number | theme | Start padding per level / of the top level, in px. | Unreleased |
collapsed | boolean | false | Icons only; labels in tooltips, submenus in popups. | Unreleased |
collapsedWidth | number | theme (48 / 52) | Width in px while collapsed. | Unreleased |
iconSize / collapsedIconSize | number | theme | Icon size in px, expanded / collapsed. | Unreleased |
disabled | boolean | false | Disable every row. | Unreleased |
renderLabel | (option) => ReactNode | undefined | Replaces the label of rows and group titles. | Unreleased |
renderLink | (props: MenuLinkProps, option) => ReactNode | undefined | Renders rows with an href. Defaults to an <a>. | Unreleased |
Any other prop, and ref, goes to the <nav>.
MenuOption
| Name | Type | Description |
|---|---|---|
key | string | number | Required and unique. |
label | ReactNode | The row’s text. |
icon | ReactNode | Before the label; alone when collapsed. |
extra | ReactNode | At the end of the row. |
href | string | Makes the row a link. Ignored on submenus. |
disabled | boolean | Can’t be chosen or opened. |
show | boolean | false leaves the option out. |
children | MenuMixedOption[] | Makes the row a submenu. |
Groups are { type: "group", key, label, children } and dividers { type: "divider", key }.
Notes
- Only
mode="vertical"for now; a horizontal top bar is planned separately. - Labels, icons and extras are
ReactNodes. For links, usehref+renderLinkrather than a router link inside the label, so the whole row is the link. - A group’s rows stay at the group title’s depth, as in most docs sidebars.
accordionapplies at every level.