Skip to content
OpenPerf
Esc
↑↓navigate↵open⌘Jpreview
On this page

Menu

A list of actions behind a button.

import { Button, Menu } from "@openperf/deft";

import { Preview } from "../../src/Preview";

export default function MenuBasic() {
  return (
    <Preview overlay={true}>
      <Menu.Root>
        <Menu.Trigger render={<Button.Root />}>Open menu</Menu.Trigger>
        <Menu.Content>
          {Array.from({ length: 3 }, (_, index) => (
            <Menu.Item key={index}>Item {index + 1}</Menu.Item>
          ))}
        </Menu.Content>
      </Menu.Root>
    </Preview>
  );
}
  • Use a menu for secondary actions that don’t fit in a toolbar. To pick a value for a form, use a Select.
  • Separate groups of ordinary actions with Menu.Separator.
  • Put the destructive action last and set tone="danger" on it.
  • In menus opened from the action bar, set size="sm" on every item to match its smaller buttons.
  • Menu.CheckboxItem keeps the menu open on click, so several options can be toggled in a row.

Item content

import { Button, Menu } from "@openperf/deft";
import { IconPlus } from "@tabler/icons-react";

import { Preview } from "../../src/Preview";

export default function MenuItemContent() {
  return (
    <Preview overlay={true}>
      <Menu.Root>
        <Menu.Trigger render={<Button.Root />}>Open menu</Menu.Trigger>
        <Menu.Content>
          <Menu.Item>
            <IconPlus />
            Item 1
          </Menu.Item>
          <Menu.Item description="Description">Item 2</Menu.Item>
          <Menu.Item trailing="12">Item 3</Menu.Item>
        </Menu.Content>
      </Menu.Root>
    </Preview>
  );
}

Render an icon as the first child to make a familiar action easier to scan, and add a description only when the action needs more context. Put a keyboard shortcut or a count in trailing, kept secondary to the label. Icons take the item tone, including danger.

Radio groups

import { Button, Menu } from "@openperf/deft";

import { Preview } from "../../src/Preview";

export default function MenuRadioGroups() {
  return (
    <Preview overlay={true}>
      <Menu.Root>
        <Menu.Trigger render={<Button.Root />}>Open menu</Menu.Trigger>
        <Menu.Content>
          <Menu.RadioGroup defaultValue="option-1">
            <Menu.GroupLabel>Label</Menu.GroupLabel>
            {Array.from({ length: 3 }, (_, index) => (
              <Menu.RadioItem key={index} value={`option-${index + 1}`}>
                Option {index + 1}
              </Menu.RadioItem>
            ))}
          </Menu.RadioGroup>
        </Menu.Content>
      </Menu.Root>
    </Preview>
  );
}

Use Menu.RadioGroup for mutually exclusive choices; the selected item shows a check on the right. Menu.GroupLabel names the choice set, such as Sort by. Use separators rather than labels for groups of ordinary actions.

API

PropType
align?"start" | "center" | "end"

On Content.

Type"start" | "center" | "end"
Default"start"
side?"top" | "bottom" | "left" | "right" | "inline-start" | "inline-end"

On Content.

Type"top" | "bottom" | "left" | "right" | "inline-start" | "inline-end"
Default"bottom"
sideOffset?number

On Content.

Typenumber
Default4
tone?"default" | "danger"

On Item.

Type"default" | "danger"
Default"default"
size?"sm" | "md"

On Item, CheckboxItem and RadioItem.

Type"sm" | "md"
Default"md"
description?ReactNode

On Item, CheckboxItem, RadioItem and SubmenuTrigger. A second line in muted text.

TypeReactNode
trailing?ReactNode

On Item and CheckboxItem. A shortcut or count on the right.

TypeReactNode
closeOnClick?boolean

On CheckboxItem.

Typeboolean
Defaultfalse

Parts: Root, CheckboxItem, Content, Footer, GroupLabel, Item, RadioItem, Separator, SubmenuTrigger, plus the other Base UI Menu parts, such as Trigger, RadioGroup, Group and SubmenuRoot. Content wraps Portal, Positioner and Popup; Footer passes its props to the native div. Built on Base UI Menu.

Was this page helpful?