Dropdown menu

Import
import { DropdownMenu } from "reshaped";
import type { DropdownMenuProps } from "reshaped";

DropdownMenu exposes multiple compound components that you can use to achieve any menu composition you need. They are all available through the DropdownMenu component.

<DropdownMenu>
  <DropdownMenu.Trigger>
    {(attributes) => <Button attributes={attributes}>Open</Button>}
  </DropdownMenu.Trigger>
  <DropdownMenu.Content>
    <DropdownMenu.Item>Action 1</DropdownMenu.Item>
    <DropdownMenu.Item>Action 2</DropdownMenu.Item>
  </DropdownMenu.Content>
</DropdownMenu>

DropdownMenu itself serves as the root wrapper for the component.

  • DropdownMenu.Trigger is a trigger providing all required attributes and handlers for your component, like Button in this example.
  • DropdownMenu.Content is a wrapper for the overlaying content with menu items.

Both components work similarly to how the Popover components work but provide additional features compared to the default Popover and MenuItem implementations.

  • DropdownMenu.Item implementation is based on the MenuItem component. It has className, size, and roundedCorners properties predefined and supports all other MenuItem properties.

DropdownMenu supports most of the properties Popover does, allowing you to use its event handlers or position it differently. It will automatically try to fit into the viewport, using the position you pass as a default.

You can control which positions should be used as fallbacks with the fallbackPositions array. Pass false or an empty array to lock the position and prevent any fallbacks from being used.

<DropdownMenu position="top-end" onClose={() => console.log("Closed")}>
  <DropdownMenu.Trigger>
    {(attributes) => <Button attributes={attributes}>Open</Button>}
  </DropdownMenu.Trigger>
  <DropdownMenu.Content>
    <DropdownMenu.Item>Action 1</DropdownMenu.Item>
    <DropdownMenu.Item>Action 2</DropdownMenu.Item>
  </DropdownMenu.Content>
</DropdownMenu>

DropdownMenu supports separating items into sections using the DropdownMenu.Section component.

<DropdownMenu>
  <DropdownMenu.Trigger>
    {(attributes) => <Button attributes={attributes}>Open</Button>}
  </DropdownMenu.Trigger>
  <DropdownMenu.Content>
    <DropdownMenu.Section>
      <DropdownMenu.Item>Action 1</DropdownMenu.Item>
      <DropdownMenu.Item>Action 2</DropdownMenu.Item>
    </DropdownMenu.Section>
    <DropdownMenu.Section>
      <DropdownMenu.Item>Action 3</DropdownMenu.Item>
      <DropdownMenu.Item>Action 4</DropdownMenu.Item>
    </DropdownMenu.Section>
  </DropdownMenu.Content>
</DropdownMenu>

You can create one or multiple submenus inside your DropdownMenu using DropdownMenu.SubMenu and DropdownMenu.SubTrigger. It automatically handles focus trapping for multiple levels of nesting.

<DropdownMenu>
  <DropdownMenu.Trigger>
    {(attributes) => <Button attributes={attributes}>Open</Button>}
  </DropdownMenu.Trigger>
  <DropdownMenu.Content>
    <DropdownMenu.Item onClick={() => {}}>Item 1</DropdownMenu.Item>
    <DropdownMenu.SubMenu>
      <DropdownMenu.SubTrigger>Item 2</DropdownMenu.SubTrigger>
      <DropdownMenu.Content>
        <DropdownMenu.Item onClick={() => {}}>SubItem 1</DropdownMenu.Item>
        <DropdownMenu.Item onClick={() => {}}>SubItem 2</DropdownMenu.Item>
      </DropdownMenu.Content>
    </DropdownMenu.SubMenu>
    <DropdownMenu.Item onClick={() => {}}>Item 3</DropdownMenu.Item>
  </DropdownMenu.Content>
</DropdownMenu>

You can either pass a literal width value like 200px or a trigger value to align the DropdownMenu content width with its trigger. This is helpful when using a fullWidth Button component or Select / TextField together with DropdownMenu.

<DropdownMenu width="trigger">
  <DropdownMenu.Trigger>
    {(attributes) => (
      <Select
        name="animal"
        placeholder="Select an animal"
        inputAttributes={attributes}
      />
    )}
  </DropdownMenu.Trigger>
  <DropdownMenu.Content>
    <DropdownMenu.Item>Turtle</DropdownMenu.Item>
    <DropdownMenu.Item>Cat</DropdownMenu.Item>
  </DropdownMenu.Content>
</DropdownMenu>

If you need to add a close button to the DropdownMenu content, you can use it with the DropdownMenu.Dismissible utility. It automatically triggers the close event of the DropdownMenu when clicked.

<DropdownMenu width="trigger">
  <DropdownMenu.Trigger>
    {(attributes) => (
      <Select
        name="animal"
        placeholder="Select an animal"
        inputAttributes={attributes}
      />
    )}
  </DropdownMenu.Trigger>
  <DropdownMenu.Content>
    <View padding={3} paddingBlock={2}>
      <DropdownMenu.Dismissible closeAriaLabel="Close menu">
        <Text weight="bold">Animals list</Text>
      </DropdownMenu.Dismissible>
    </View>
    <DropdownMenu.Item>Turtle</DropdownMenu.Item>
    <DropdownMenu.Item>Cat</DropdownMenu.Item>
  </DropdownMenu.Content>
</DropdownMenu>

If you pass the defaultActive flag to the component, it will open the menu on mount and use its internal state afterward.

If you pass the active flag, the controlled mode will be activated, and you will need to manage the component's state manually. This means you must control the state using onOpen and onClose handlers, allowing you to add custom logic before updating the state.

  • ArrowUp / ArrowDown - navigate the menu items
  • Enter / Space - select the focused menu item
  • Esc - close the dropdown
  • Tab - close the dropdown and move the focus to the next element after the trigger
  • Shift + Tab - close the dropdown and keep the focus on the trigger
  • ArrowRight / Enter - open the submenu if a subtrigger is focused
  • ArrowLeft / Esc - close the submenu
  • It's essential to pass the attributes provided by the DropdownMenu component to your interactive trigger component. This ensures that all user events and aria attributes are assigned correctly.
Previous