Dropdown Menu

Jump to Props

Displays a menu to the user such as a set of actions or functions

Import

import { DropdownMenu } from "@vitality-ds/components";

Usage

A dropdown menu reveals its options once it has been clicked on. This enables maximum space efficiency while still offering many options. The component takes two props: trigger and items. Items represents the menu items within the dropdown, and trigger represents the element you click on to open the menu. Regardless of type, all dropdown menus list one or more menu items, each of which represents a command, option, or state that affects the current selection or context. When users choose a menu item, the action occurs and the menu typically closes.

() => {
  const trigger = {
    type: "button",
    props: {
      children: "Add...",
      appearance: "primary",
    },
  };

  const items = [
    {
      label: "Attachment",
    },
    {
      label: "Consult Note",
    },
  ];
  return <DropdownMenu trigger={trigger} items={items} />;
};

Content

Basic Items

Each menu item that can be interacted with should have a descriptive label that is easy to understand.

Use "..." after a label when an action requires a follow-up step.

() => {
  const trigger = {
    type: "button",
    props: {
      children: "Add",
      appearance: "primary",
    },
  };

  const items = [
    {
      label: "Attachment",
      onClick: () => console.log("clicked"),
    },
    {
      label: "Consult Note",
      onClick: () => console.log("clicked"),
    },
    {
      label: "Custom Letter...",
      onClick: () => console.log("clicked"),
    },
  ];
  return <DropdownMenu trigger={trigger} items={items} />;
};

Icons

Icons can be added to both the trigger component and/or dropdown menu items. Use icons to add extra visual support in understanding the result of a chosen action. For instance, in an "Add..." dropdown, where multiple types of entities can be added, you can use icons to provide extra visual support – each icon indicating what type of entity will be added.

As a rule of thumb, if you add one icon to a dropdown menu, then all items should have an icon. If you don't adhere to this, there will be misalignment of the labels.

() => {
  const trigger = {
    type: "button",
    props: {
      children: "Add",
      iconRight: <Add />,
      appearance: "primary",
    },
  };

  const items = [
    {
      label: "Attachment",
      icon: <Attachment />,
      onClick: () => console.log("clicked"),
    },
    {
      label: "Consult Note",
      icon: <ConsultNote />,
      onClick: () => console.log("clicked"),
    },
  ];
  return <DropdownMenu trigger={trigger} items={items} />;
};

Keyboard Shortcuts

The DropdowMenu component does not provide the actual keyboard shortcut functionality, however it does allow you to indicate what shortcut an item has. Do this by passing an array of keys to the shortcut property. For more information, see the <Shortcuts /> component.

() => {
  const trigger = {
    type: "button",
    props: {
      children: "Add",
      iconRight: <Add />,
      appearance: "primary",
    },
  };

  const items = [
    {
      label: "Attachment",
      icon: <Attachment />,
      shortcut: ["⌘", "A"],
      onClick: () => console.log("clicked"),
    },
    {
      label: "Consult Note",
      icon: <ConsultNote />,
      shortcut: ["⌘", "N"],
      onClick: () => console.log("clicked"),
    },
  ];
  return <DropdownMenu trigger={trigger} items={items} />;
};

Disabling a menu item

If an action cannot be performed, pass the disabled prop to it.

() => {
  const trigger = {
    type: "button",
    props: {
      children: "Add",
      iconRight: <Add />,
      appearance: "primary",
    },
  };

  const items = [
    {
      label: "Attachment",
      icon: <Attachment />,
      disabled: true,
      onClick: () => console.log("clicked"),
    },
    {
      label: "Consult Note",
      icon: <ConsultNote />,
      onClick: () => console.log("clicked"),
    },
  ];
  return <DropdownMenu trigger={trigger} items={items} />;
};

Custom Menu Items

Custom menu items can be passed to items. This should be used with caution and is the developers responsibility to ensure best practices around UX and development are followed.

() => {
  const CustomItem = () => (
    <DocsBox css={{ padding: "$md" }}>
      <Typography variant="sectionSubtitle">Dr Andrew Demo</Typography>
      <Typography>Brisbane Medical Centre · GTU99999</Typography>
    </DocsBox>
  );

  const trigger = {
    type: "button",
    props: { children: "Add" },
  };

  const items = [
    {
      type: "custom",
      component: <CustomItem />,
    },
    { type: "divider" },
    {
      label: "Menu item",
      onClick: () => alert("clicked"),
    },
    {
      label: "Menu item",
      onClick: () => alert("clicked"),
    },
  ];

  return <DropdownMenu items={items} trigger={trigger} />;
};

Handling Click actions

Each dropdown menu item is essentially an action button. Clicking a menu item should perform an action and you should avoid navigating the user away from the page.

() => {
  const [chosenAction, setChosenAction] = React.useState();
  const trigger = {
    type: "button",
    props: {
      children: "Add",
      iconRight: <Add />,
      appearance: "primary",
    },
  };

  const items = [
    {
      label: "Attachment",
      icon: <Attachment />,
      onClick: () => setChosenAction("Attachment"),
    },
    {
      label: "Consult Note",
      icon: <ConsultNote />,
      onClick: () => setChosenAction("Consult Note"),
    },
  ];
  return (
    <Stack>
      <DropdownMenu trigger={trigger} items={items} />
      {chosenAction && <>User clicked: {chosenAction}</>}
    </Stack>
  );
};

Some actions performed by a dropdown menu item may require the user to navigate to a different page. In this case, set type="link" to an item, and you can use the href prop to provide a link to the page or the onClick prop is necessary for router libraries. The dropdown menu item can receie all valid <a> props, such as target, rel, etc.

() => {
  const trigger = {
    type: "button",
    props: {
      children: "Links...",
    },
  };

  const items = [
    {
      type: "link",
      label: "Patients",
      href: "https://wikipedia.com/wiki/Patient",
      target: "_blank",
    },
    {
      type: "link",
      label: "Medications",
      href: "https://wikipedia.com/wiki/Medication",
      target: "_blank",
    },
  ];
  return <DropdownMenu trigger={trigger} items={items} />;
};

Use Submenus when you have to "drill down" into more specific actions. For example, an "Open Recent..." action could show a submenu that displays a list of recently opened patient files. Whilst submenus can offer more options, they do add a layer of complexity to the menu and should be used sparingly.

Vitality has restricted the number of submenus to one – as nested submenus can create a complicated user experience.

() => {
  const trigger = {
    type: "button",
    props: {
      children: "File",
      appearance: "primary",
    },
  };

  const Container = (props) => (
    <DocsBox
      css={{ width: "100%", backgroundColor: "$neutral1", padding: "$lg" }}
      {...props}
    />
  );

  const items = [
    {
      label: "New",
      shortcut: ["⌘", "N"],
    },
    {
      type: "divider",
    },
    {
      label: "Open...",
      shortcut: ["⌘", "O"],
    },
    {
      label: "Open Recent",
      type: "subMenu",
      subMenu: [
        { label: "Aubrey McClusky" },
        { label: "Darlene Johnson" },
        { label: "Daphne Nonsemple" },
      ],
    },
    {
      label: "Re-open Closed Patient",
      shortcut: ["⌘", "⇧", "O"],
    },
  ];
  return (
    <Container>
      <DropdownMenu trigger={trigger} items={items} />
    </Container>
  );
};

Trigger component

The trigger prop takes an object which determines the type of trigger. It can be either a <Button>, <IconButton /> or an <Avatar>, and the props that component type requires. For a complete list of available props, see their respective documentation.

() => {
  const items = [
    { label: "Account Settings", icon: <Settings /> },
    { label: "Log Out", icon: <Logout /> },
  ];
  return (
    <Stack direction="horizontal" align="center">
      <DropdownMenu
        trigger={{
          type: "button",
          props: { appearance: "primary", children: "Add..." },
        }}
        items={items}
      />
      <DropdownMenu
        trigger={{
          type: "iconButton",
          props: {
            icon: <MenuMoreVertical />,
            tooltipContent: "Additional Options",
          },
        }}
        items={items}
      />
      <DropdownMenu
        trigger={{
          type: "avatar",
          props: {
            size: "lg",
            color: "primary",
            initials: "XA",
            tooltipContent: "Account Settings & Log Out",
          },
        }}
        items={items}
      />
    </Stack>
  );
};

Displaying an arrow in the trigger

When a dropdown's trigger will appear among other button elements, consider including <Dropdown /> icon in the button to indicate that the button will reveal a dropdown.

() => {
  const trigger = {
    type: "button",
    props: {
      children: "Add",
      iconRight: <Dropdown />,
      appearance: "primary",
    },
  };

  const items = [
    {
      label: "Attachment",
      onClick: () => console.log("clicked"),
    },
    {
      label: "Consult Note",
      onClick: () => console.log("clicked"),
    },
  ];
  return <DropdownMenu trigger={trigger} items={items} />;
};

Disabling the trigger

If you pass the disabled prop to the dropdown menu component, it will disable both the dropdown popover and the trigger element's clickability, while also reflecting the disabled styling of the trigger element.

() => {
  const trigger = {
    type: "button",
    props: {
      children: "Add...",
      appearance: "primary",
    },
  };

  const items = [
    {
      label: "Attachment",
    },
    {
      label: "Consult Note",
    },
  ];
  return <DropdownMenu disabled trigger={trigger} items={items} />;
};

Organising Menu Items

To organise the items in a dropdown menu, arrange them in a logical order with the most commonly used option at the top, if it is known. For lengthy lists, group similar menu items together (for example, 'cut', 'copy' and 'paste' are often grouped together in a dropdown menu). If incorporating checkable items, attempt to group associated actions.

Dividing Items

Use a divider to create a logical separation between dropdown menu items. This can help the user to easily scan the items and identify groups of common actions.

() => {
  const items = [
    { label: "New Patient...", shortcut: ["⌘", "N"] },
    { type: "divider" },
    { label: "Open...", shortcut: ["⌘", "O"] },
    { label: "Open Recent..." },
    { type: "divider" },
    { label: "Save", shortcut: ["⌘", "S"] },
    { label: "Save As...", shortcut: ["⌘", "⇧", "O"] },
  ];

  return (
    <DropdownMenu
      trigger={{ type: "button", props: { children: "View as..." } }}
      items={items}
    />
  );
};

Grouping Items

As an extension of dividing menu items, you can also add more context to a grouping by adding a groupLabel. Group the items by using a brief label that summarises the choices in that particular subcategory.

() => {
  const items = [
    { type: "groupLabel", label: "Priority Patients" },
    { label: "Aubrey McClusky" },
    { label: "Max Payne" },
    { type: "groupLabel", label: "Recently Viewed Patients" },
    { label: "Darlene Johnson" },
    { label: "Daphne Nonsemple" },
    { label: "Sarah Smith" },
    { type: "divider" },
    { label: "Close Patient File", shortcut: ["⌘", "Q"] },
    { label: "New Patient...", shortcut: ["⌘", "N"] },
  ];

  return (
    <DropdownMenu
      trigger={{ type: "button", props: { children: "View as..." } }}
      items={items}
    />
  );
};

You do not need to group all items in a menu. Adding a group label helps provide context to a specific group when useful. When other items appear below a group, use a divider to show a clear separation

Checkable items

If the item can be checked or unchecked (such as toggling a user preference), you can use type="checkableItem", this will display a check mark next to the menu item when selected.

The state of checkable items is a controlled component. This means that your application should manage their state, triggered by the onClick prop and providing the boolean state into the checked prop.

() => {
  const [showRecentPrescriptions, setShowRecentPrescriptions] =
    React.useState(true);
  const [showDeletedAppointments, setShowDeletedAppointments] =
    React.useState(false);

  const items = [
    { label: "New Patient" },
    { label: "Edit Patient" },
    { type: "divider" },
    {
      label: "Include Pregnancy Record",
      type: "checkableItem",
      checked: showDeletedAppointments,
      onClick: () => setShowDeletedAppointments(!showDeletedAppointments),
      shortcut: ["⌘", "P"],
    },
    {
      label: "Show Recent Prescriptions",
      type: "checkableItem",
      checked: showRecentPrescriptions,
      onClick: () => setShowRecentPrescriptions(!showRecentPrescriptions),
      shortcut: ["⌘", "R"],
    },
  ];

  return (
    <DropdownMenu
      trigger={{ type: "button", props: { children: "View as..." } }}
      items={items}
    />
  );
};

Complex Example

() => {
  const trigger = {
    type: "button",
    props: {
      children: "Add",
      iconRight: <Add />,
      appearance: "primary",
    },
  };

  const items = [
    {
      label: "Attachment",
      icon: <Attachment />,
      shortcut: ["⌘", "A"],
    },
    {
      type: "divider",
    },
    {
      label: "Consult Note",
      icon: <ConsultNote />,
      disabled: true,
      shortcut: ["⌘", "C"],
    },
    {
      type: "subMenu",
      icon: <Medications />,
      label: "Medications",
      subMenu: [
        {
          label: "Prescription",
          onClick: (event) => console.log(event.target),
        },
        { label: "ePrescription" },
        { label: "View List" },
      ],
    },
    {
      label: "Letter",
      icon: <Letter />,
      shortcut: ["⌘", "L"],
    },
    {
      label: "Procedure",
      icon: <Procedure />,
      shortcut: ["⌘", "P"],
    },
    {
      label: "Pregnancy Record",
      icon: <Obstetrics />,
      shortcut: ["⌘", "P"],
    },
    {
      type: "divider",
    },
    {
      label: "Quote",
      icon: <Billed />,
      shortcut: ["⌘", "Q"],
    },
    {
      label: "Invoice",
      icon: <Invoice />,
      shortcut: ["⌘", "I"],
    },
    {
      type: "divider",
    },
    {
      label: "Recall",
      icon: <Recall />,
      shortcut: ["⌘", "R"],
    },

    {
      label: "Request",
      icon: <Request />,
      shortcut: ["⌘", "⇧", "R"],
    },
    {
      type: "divider",
    },
    {
      label: "To Do",
      icon: <ToDo />,
      shortcut: ["⌘", "T"],
    },
  ];
  return <DropdownMenu trigger={trigger} items={items} />;
};

Figma

Figma.logo

Further Reading

Apple's Human Interface Guidelines

Props

disabled

Description

Prevent the Dropdown's trigger from being pressed, therefore preventing the dropdown from showing

Type

boolean

itemsRequired

Type

DropdownMenuItems<K>[]

preferredAlignment

Description

The preferred alignment against the trigger. May change when collisions occur.

Type

"start" | "center" | "end"

Default Value

start

triggerRequired

Type

Trigger<T>

disabled

Description

Determines if the menu item can be clicked or not

Type

boolean

htmlTag

Type

"a"

icon

Description

The optional icon that renders next to the menu item.

Type

ItemIconType

indentContent

Type

boolean

label

Description

The text that renders in the dropdown menu for the user to interact with.

Type

string

onClick

Description

The function performed when a user clicks on a menu item.

Type

MouseEventHandler<HTMLDivElement>

shortcut

Description

Array of strings that represent keyboard shortcuts.

Type

string[]

subMenu

Description

Array of submenu items, in the same structure as root level menu items.

Type

DropdownMenuItem[]

CheckableDropdownMenuItem

checked

Description

Determines whether the item is checked or not

Type

boolean

disabled

Description

Determines if the menu item can be clicked or not

Type

boolean

htmlTag

Type

("a" | ({ "@bp1"?: "a"; "@bp2"?: "a"; "@bp3"?: "a"; "@initial"?: "a"; } & { [x: string]: "a"; })) & ("a"

icon

Description

The optional icon that renders next to the menu item.

Type

ItemIconType

indentContent

Type

(boolean | "true" | ({ "@bp1"?: boolean | "true"; "@bp2"?: boolean | "true"; "@bp3"?: boolean | "true"; "@initial"?: boolean | "true"; } & { [x: string]: boolean | "true"; })) & (boolean | "true"

label

Description

The text that renders in the dropdown menu for the user to interact with.

Type

string

onClick

Description

The function performed when a user clicks on a menu item. The function performed when a user clicks on a checkable menu item.

Type

MouseEventHandler<HTMLDivElement> & ((checked: boolean) => void)

shortcut

Description

Array of strings that represent keyboard shortcuts.

Type

string[]

subMenu

Description

Array of submenu items, in the same structure as root level menu items.

Type

DropdownMenuItem[]

© 2025