Usage

When to use Menu, what its main props do, and the choices it leaves to you.

When to use

Menu is a list of destinations laid out vertically, horizontally or nested inline, with one of them current. It keeps track of the highlight and the open submenus. Your page decides what the keys mean.
  • A sidebar of destinations, with sections and a second level of submenus. Use mode="inline".
  • A navigation bar across the top of the page. Use mode="horizontal", the only mode where items that do not fit move into an overflow submenu.
  • A list of actions with no current item. selectable={false} keeps onClick and drops the highlight.
Use something else when:
  • Dropdown for a short list of actions that opens from a trigger. It renders a Menu from its menu={{ items }} prop and handles the trigger, the placement and closing for you.
  • Tabs when the entries are peers in one view and each one has a panel. Tabs brings the tab roles and the panel wiring.
  • Steps when the entries have an order and a completion state. A menu lists places. Steps shows where you are in a sequence.
  • Segmented when one of a short fixed set changes how a single view is shown. That is a setting, not a destination.

Mode decides where a submenu goes

mode decides whether a submenu expands in place or opens as a popup. It defaults to vertical.
  • inline is the sidebar shape. A submenu expands in place and pushes the items below it down, so the menu grows as submenus open. It is the only mode inlineCollapsed works with.
  • vertical looks like inline until you hover a submenu. The submenu opens as a popup to the side, so the menu keeps its height however deep the tree is.
  • horizontal is the header shape. Submenus open below their item as popups, and items that do not fit move into one trailing submenu.
Pick the mode from the layout. Switching away from inline closes every submenu and calls onOpenChange with an empty array. They open again when you switch back, so a menu that changes mode at a breakpoint loses its open submenus on the way out and gets them back on the way in.

Build the menu from items

The <Menu.Item> children form still works, but it logs a deprecation warning. items is an array, so you can map it from your route table. Menu.Item, Menu.SubMenu and Menu.Divider are still exported to help you migrate.
  • An entry's type decides what it is: "group", "divider", or nothing for a plain item.
  • An entry with children and no type is a submenu. A group has children too, so check type when one shows up as the other.
  • null is allowed in the array, so a conditional entry can stay inline (isAdmin ? adminItem : null) with no filter afterwards.
  • Every other prop speaks in keys: selectedKeys, openKeys and every callback. Derive them from the route, never from the array index, or reordering the menu moves the highlight.

selectedKeys and openKeys are separate state

Which item is selected and which submenus are open are two separate pieces of state. Controlling one does not control the other.
  • openKeys needs onOpenChange. Passing openKeys alone keeps every submenu shut.
  • For navigation, control selectedKeys from your router and let the menu keep its own open submenus with defaultOpenKeys. Opening a submenu is not a navigation and does not belong in the URL.
  • To keep one submenu open at a time, control openKeys and drop the siblings in onOpenChange. There is no accordion prop.
  • Use selectable={false} for a list of actions. Clicks still fire onClick, and no item stays highlighted. multiple lets more than one item stay selected.
  • Keep it to two levels. A third level indents another inlineIndent in inline mode, and in the other modes it is a popup opening from a popup, which is hard to reach with a pointer.
  • A submenu title is not a destination. Clicking it opens the submenu and nothing else, so a section that needs a landing page gets that page as its first child.
  • triggerSubMenuAction defaults to hover, with subMenuOpenDelay and subMenuCloseDelay at 0.1s each. Switch it to click where the menu is dense or touch is the main input.
  • A submenu popup renders in a portal, so a selector written on the root does not reach it. Use classNames.popup, overflowedIndicatorPopupClassName or getPopupContainer.
  • forceSubMenuRender mounts every submenu up front. Use it only when something inside a closed submenu has to exist, such as a form field that must submit. It renders the whole tree on first paint.

Groups and dividers

  • { type: "group", label, children } is a heading with items beneath it. Its children stay at the parent's depth: a group labels a run of items and does not nest them.
  • Reach for a group before a submenu when the section is short enough to stay open. A group costs one heading line. A submenu costs a click, a piece of state and a chance to get lost.
  • { type: "divider" } draws a line, and dashed: true makes it dashed. It separates without labeling, and more than two in one menu stop helping.
  • Use a group heading where the items below have something in common. A reader can see a gap, but only a heading tells them what the gap means.

inlineCollapsed, and the Sider

  • inlineCollapsed only works with mode="inline", and logs a warning in development otherwise.
  • Leave inlineCollapsed unset inside a Layout.Sider. The Sider collapses the menu for you, and setting both stops the menu following the Sider.
  • A collapsed menu behaves like mode="vertical": every submenu opens as a popup, and the submenus you had open come back when you expand it.
  • Give every top-level item an icon: a collapsed item without one shows the first letter of its label.
  • While collapsed, each item shows its label in a tooltip on hover, with nothing to set up. Pass tooltip to configure it, or tooltip={false} to turn it off.

Theme is not the page mode

  • theme takes light (the default) or dark, whether the page is in light or dark mode. A dark menu on a light page is the usual admin sidebar.
  • Set theme on a submenu item to change only that submenu's popup. Use it for a different region of the app, not to emphasize one entry.
  • Menu has no variant or emphasis prop. theme is how you make it stand out.
  • A dark menu on a light page puts two palettes on one screen, so check the contrast of both.

What an item should say

  • One noun phrase per item, parallel across the set. A menu is read as a column, so “Billing, Members, Integrations” scans and “Billing, Manage your members, Integrations setup” does not.
  • label takes any node, so a count or a Badge can sit beside the text. Keep it to one extra per row, and take the count from the same source the destination shows.
  • A long label wraps the row onto two lines and breaks the rhythm of the column. Shorten the word before you widen the menu.
  • An item's title is the DOM title attribute and the fallback text of the collapsed tooltip. It is not the label, and it is not an accessible name.
  • An icon repeats the label and does not replace it. A collapsed menu is the exception, where the icon is all there is, which is why every top-level item needs one.