Skip to main content
react-md

Menu

A menu displays a list of choices on a temporary surface. They appear when users interact with a button, action, or other control.

Simple Example

A menu can be created by using the DropdownMenu and MenuItem components. The DropdownMenu requires the buttonChildren prop to render text, icons, or other content in a button and the available MenuItems as children.

import { DropdownMenu } from "@react-md/core/menu/DropdownMenu";
import { MenuItem } from "@react-md/core/menu/MenuItem";
import { type ReactElement } from "react";

export default function SimpleExample(): ReactElement {
  return (
    <DropdownMenu buttonChildren="Dropdown">
      <MenuItem>Item 1</MenuItem>
      <MenuItem>Item 2</MenuItem>
      <MenuItem>Item 3</MenuItem>
    </DropdownMenu>
  );
}

Press Enter to start editing.

MenuItems should normally provide an onClick event handler

The last clicked item is: none

"use client";

import { Box } from "@react-md/core/box/Box";
import { DropdownMenu } from "@react-md/core/menu/DropdownMenu";
import { MenuItem } from "@react-md/core/menu/MenuItem";
import { Typography } from "@react-md/core/typography/Typography";
import { type ReactElement, useState } from "react";

export default function MenuitemActionExample(): ReactElement {
  const [lastClicked, setLastClicked] = useState("");
  return (
    <Box stacked align="start">
      <DropdownMenu buttonChildren="Dropdown">
        <MenuItem
          onClick={() => {
            setLastClicked("Item 1");
          }}
        >
          Item 1
        </MenuItem>
        <MenuItem
          onClick={() => {
            setLastClicked("Item 2");
          }}
        >
          Item 2
        </MenuItem>
        <MenuItem
          onClick={() => {
            setLastClicked("Item 3");
          }}
        >
          Item 3
        </MenuItem>
      </DropdownMenu>
      <Typography>
        The last clicked item is: <code>{lastClicked || "none"}</code>
      </Typography>
    </Box>
  );
}

Press Enter to start editing.

Horizontal Menu

A menu can be rendered horizontally instead of vertically by enabling the horizontal prop.

import { DropdownMenu } from "@react-md/core/menu/DropdownMenu";
import { MenuItem } from "@react-md/core/menu/MenuItem";
import { type ReactElement } from "react";

export default function HorizontalMenuExample(): ReactElement {
  return (
    <DropdownMenu buttonChildren="Dropdown" horizontal>
      <MenuItem>Item 1</MenuItem>
      <MenuItem>Item 2</MenuItem>
      <MenuItem>Item 3</MenuItem>
    </DropdownMenu>
  );
}

Press Enter to start editing.

Button Styling

Most of the Button props are available as top-level props on the DropdownMenu.

import { DropdownMenu } from "@react-md/core/menu/DropdownMenu";
import { MenuItem } from "@react-md/core/menu/MenuItem";
import MoreVertOutlinedIcon from "@react-md/material-icons/MoreVertOutlinedIcon";
import { type ReactElement } from "react";

export default function ButtonStyling(): ReactElement {
  return (
    <>
      <DropdownMenu
        theme="primary"
        themeType="outline"
        buttonChildren="Options..."
      >
        <MenuItem>Item 1</MenuItem>
        <MenuItem>Item 2</MenuItem>
        <MenuItem>Item 3</MenuItem>
      </DropdownMenu>
      <DropdownMenu
        theme="secondary"
        themeType="contained"
        iconSize="small"
        buttonType="icon"
        buttonChildren={<MoreVertOutlinedIcon />}
      >
        <MenuItem>Item 1</MenuItem>
        <MenuItem>Item 2</MenuItem>
        <MenuItem>Item 3</MenuItem>
      </DropdownMenu>
    </>
  );
}

Press Enter to start editing.

Icon Button

An icon button can be rendered by setting the buttonType to "icon". This will also remove the default dropdown icon within the button.

Remember to provide an aria-label or accessible text for screen readers when using icon buttons.

import { DropdownMenu } from "@react-md/core/menu/DropdownMenu";
import { MenuItem } from "@react-md/core/menu/MenuItem";
import MoreVertOutlinedIcon from "@react-md/material-icons/MoreVertOutlinedIcon";
import { type ReactElement } from "react";

export default function IconButtonExample(): ReactElement {
  return (
    <DropdownMenu
      aria-label="Options"
      buttonType="icon"
      buttonChildren={<MoreVertOutlinedIcon />}
    >
      <MenuItem>Item 1</MenuItem>
      <MenuItem>Item 2</MenuItem>
      <MenuItem>Item 3</MenuItem>
    </DropdownMenu>
  );
}

Press Enter to start editing.

Floating Action Button

A menu can be rendered in a floating action button by providing the floating prop. Just like the Button component, the button will default to an icon button if the floating prop is defined instead of a text button.

import { DropdownMenu } from "@react-md/core/menu/DropdownMenu";
import { MenuItem } from "@react-md/core/menu/MenuItem";
import MoreVertOutlinedIcon from "@react-md/material-icons/MoreVertOutlinedIcon";
import { type ReactElement } from "react";

export default function FloatingActionButtonExample(): ReactElement {
  return (
    <>
      <DropdownMenu
        floating="top-left"
        buttonChildren={<MoreVertOutlinedIcon />}
      >
        <MenuItem>Item 1</MenuItem>
        <MenuItem>Item 2</MenuItem>
        <MenuItem>Item 3</MenuItem>
      </DropdownMenu>
      <DropdownMenu
        aria-label="Options"
        floating="top-right"
        buttonType="text"
        buttonChildren="Options"
      >
        <MenuItem>Item 1</MenuItem>
        <MenuItem>Item 2</MenuItem>
        <MenuItem>Item 3</MenuItem>
      </DropdownMenu>
    </>
  );
}

Press Enter to start editing.

A MenuItem can render addons just like the ListItem.

import { Avatar } from "@react-md/core/avatar/Avatar";
import { DropdownMenu } from "@react-md/core/menu/DropdownMenu";
import { MenuItem } from "@react-md/core/menu/MenuItem";
import FavoriteIcon from "@react-md/material-icons/FavoriteIcon";
import { type ReactElement } from "react";

export default function MenuItemAddonsExample(): ReactElement {
  return (
    <DropdownMenu buttonChildren="Dropdown">
      <MenuItem leftAddon={<FavoriteIcon />}>Item 1</MenuItem>
      <MenuItem rightAddon={<Avatar>I</Avatar>} rightAddonType="avatar">
        Item 1
      </MenuItem>
    </DropdownMenu>
  );
}

Press Enter to start editing.

Configuring Menu Behavior

The Menu uses the useFixedPositioning hook to position itself within the viewport. Check out the useFixedPositioning documentation page for available options.

Controlling Menu Visibility

If the menu's visibility must manually be controlled, provide the visible and setVisible props.

import { Button } from "@react-md/core/button/Button";
import { DropdownMenu } from "@react-md/core/menu/DropdownMenu";
import { MenuItem } from "@react-md/core/menu/MenuItem";
import { type ReactElement, useState } from "react";

export default function ControllingMenuVisibility(): ReactElement {
  const [visible, setVisible] = useState(false);
  return (
    <>
      <Button
        onClick={() => {
          setVisible(true);
        }}
      >
        Open
      </Button>
      <DropdownMenu
        buttonChildren="Options..."
        visible={visible}
        setVisible={setVisible}
      >
        <MenuItem>Item 1</MenuItem>
        <MenuItem>Item 2</MenuItem>
        <MenuItem>Item 3</MenuItem>
      </DropdownMenu>
    </>
  );
}

Press Enter to start editing.

Prevent Scroll Example

The default behavior is to reposition the Menu while the user scrolls the page until the menu is no longer in the viewport. Another approach is to prevent the user from scrolling the page while the menu is open. To enable this behavior, enable the preventScroll prop.

import { DropdownMenu } from "@react-md/core/menu/DropdownMenu";
import { MenuItem } from "@react-md/core/menu/MenuItem";
import { type ReactElement } from "react";

export default function PreventScrollExample(): ReactElement {
  return (
    <DropdownMenu buttonChildren="Options..." preventScroll>
      <MenuItem>Item 1</MenuItem>
      <MenuItem>Item 2</MenuItem>
      <MenuItem>Item 3</MenuItem>
    </DropdownMenu>
  );
}

Press Enter to start editing.

Close On Resize Example

The Menu will automatically reposition itself if the user resizes the browser. Another approach is to close the menu instead. Enable the closeOnResize prop to opt into this behavior.

import { DropdownMenu } from "@react-md/core/menu/DropdownMenu";
import { MenuItem } from "@react-md/core/menu/MenuItem";
import { type ReactElement } from "react";

export default function CloseOnResizeExample(): ReactElement {
  return (
    <DropdownMenu buttonChildren="Options..." closeOnResize>
      <MenuItem>Item 1</MenuItem>
      <MenuItem>Item 2</MenuItem>
      <MenuItem>Item 3</MenuItem>
    </DropdownMenu>
  );
}

Press Enter to start editing.

Disable Portal Example

The Menu will use the Portal by default to help with positioning issues within the viewport and temporary elements like Dialogs. The portal behavior can be disabled by enabling the disablePortal prop which will make the Menu render as the next element sibling of the menu toggle button.

import { DropdownMenu } from "@react-md/core/menu/DropdownMenu";
import { MenuItem } from "@react-md/core/menu/MenuItem";
import { type ReactElement } from "react";

export default function DisablePortalExample(): ReactElement {
  return (
    <DropdownMenu buttonChildren="Options..." disablePortal>
      <MenuItem>Item 1</MenuItem>
      <MenuItem>Item 2</MenuItem>
      <MenuItem>Item 3</MenuItem>
    </DropdownMenu>
  );
}

Press Enter to start editing.

Disable Conditional Rendering Example

The Menu will only be mounted while visible which means any internal state is reset each time the Menu is opened. Set the temporary prop to false if the Menu should remain mounted while not visible and be hidden using CSS instead.

"use client";

import { ListItem } from "@react-md/core/list/ListItem";
import { DropdownMenu } from "@react-md/core/menu/DropdownMenu";
import { MenuItem } from "@react-md/core/menu/MenuItem";
import { type ReactElement, useState } from "react";

export default function DisableConditionalRenderingExample(): ReactElement {
  return (
    <DropdownMenu buttonChildren="Options..." temporary={false}>
      <ExampleChildren />
    </DropdownMenu>
  );
}

function ExampleChildren(): ReactElement {
  const [clicked, setClicked] = useState(-1);
  return (
    <>
      <ListItem presentational>{`Last clicked: ${clicked}`}</ListItem>
      <MenuItem
        onClick={() => {
          setClicked(0);
        }}
      >
        Item 1
      </MenuItem>
      <MenuItem
        onClick={() => {
          setClicked(1);
        }}
      >
        Item 2
      </MenuItem>
      <MenuItem
        onClick={() => {
          setClicked(2);
        }}
      >
        Item 3
      </MenuItem>
    </>
  );
}

Press Enter to start editing.

Context Menu

To override the default right click behavior and display a custom context menu, use the useContextMenu hook and the Menu component. The useContextMenu will provide the required props for the Menu component and automatically position it relative to the mouse.

The context menu will also update the menu to have the following default props:

"use client";

import { TextArea } from "@react-md/core/form/TextArea";
import {
  // BELOW_INNER_LEFT_ANCHOR,
  Menu,
} from "@react-md/core/menu/Menu";
import { MenuItem } from "@react-md/core/menu/MenuItem";
import { useContextMenu } from "@react-md/core/menu/useContextMenu";
import ContentCopyOutlinedIcon from "@react-md/material-icons/ContentCopyOutlinedIcon";
import ContentCutOutlinedIcon from "@react-md/material-icons/ContentCutOutlinedIcon";
import ContentPasteOutlinedIcon from "@react-md/material-icons/ContentPasteOutlinedIcon";
import { type ReactElement } from "react";

export default function ContextMenuExample(): ReactElement {
  const { menuProps, onContextMenu } = useContextMenu();
  // const { menuProps, onContextMenu } = useContextMenu({
  //   // these are the default values
  //   anchor: BELOW_INNER_LEFT_ANCHOR,
  //   menuLabel: "Context Menu",
  //   preventScroll: true,
  //
  //   // do something custom with the context menu event
  //   onContextMenu(event) {
  //   },
  // });

  return (
    <>
      <TextArea onContextMenu={onContextMenu} placeholder="Right click me!" />
      <Menu {...menuProps}>
        <MenuItem leftAddon={<ContentCutOutlinedIcon />}>Cut</MenuItem>
        <MenuItem leftAddon={<ContentCopyOutlinedIcon />}>Copy</MenuItem>
        <MenuItem leftAddon={<ContentPasteOutlinedIcon />}>Paste</MenuItem>
      </Menu>
    </>
  );
}

Press Enter to start editing.

Nested Menus

Nested dropdown menus can be created by adding a DropdownMenu as a child of another DropdownMenu. The DropdownMenu will render as a MenuItemButton instead of as a MenuButton and allow for all the correct keyboard behavior.

import { Box } from "@react-md/core/box/Box";
import { Switch } from "@react-md/core/form/Switch";
import { DropdownMenu } from "@react-md/core/menu/DropdownMenu";
import { MenuItem } from "@react-md/core/menu/MenuItem";
import { type ReactElement, useState } from "react";

export default function NestedMenusExample(): ReactElement {
  const [horizontal, setHorizontal] = useState(false);

  return (
    <Box stacked>
      <Switch
        label="Horizontal?"
        checked={horizontal}
        onChange={(event) => {
          setHorizontal(event.currentTarget.checked);
        }}
      />
      <DropdownMenu buttonChildren="Dropdown" horizontal={horizontal}>
        <MenuItem>Item 1</MenuItem>
        <MenuItem>Item 2</MenuItem>
        <DropdownMenu buttonChildren="Item 3">
          <MenuItem>Item 1</MenuItem>
          <MenuItem>Item 2</MenuItem>
          <MenuItem>Item 3</MenuItem>
        </DropdownMenu>
        <MenuItem>Item 4</MenuItem>
        <MenuItem>Item 5</MenuItem>
      </DropdownMenu>
    </Box>
  );
}

Press Enter to start editing.

Rendering in a Sheet

It can sometimes be useful to render a Menu within a Sheet instead of a temporary popup element when on smaller viewports or dealing with large menu items.

This can be configured on an app-level using the MenuConfigurationProvider or a specific DropdownMenu/Menu by providing a renderAsSheet prop. This can be set to "phone" to only render menus within sheets on the phone app size or supports true/false values if custom logic is required to render as a sheet instead.

import { DropdownMenu } from "@react-md/core/menu/DropdownMenu";
import { MenuConfigurationProvider } from "@react-md/core/menu/MenuConfigurationProvider";
import { MenuItem } from "@react-md/core/menu/MenuItem";
import { type ReactElement } from "react";

export default function RenderingInASheet(): ReactElement {
  return (
    <>
      <MenuConfigurationProvider
        renderAsSheet
        // renderAsSheet={false}
        // renderAsSheet="phone"
      >
        <DropdownMenu buttonChildren="Dropdown">
          <MenuItem disabled>Item 1</MenuItem>
          <MenuItem>Item 2</MenuItem>
          <MenuItem>Item 3</MenuItem>
          <MenuItem>Item 4</MenuItem>
          <MenuItem>Item 5</MenuItem>
        </DropdownMenu>
      </MenuConfigurationProvider>
    </>
  );
}

Press Enter to start editing.

Sheet Options

The Sheet can be configured further to support an optional header, an optional footer, the position within the viewport, and the vertical size. The optional header or footer components can be updated to control the visibility of the menu as well by using the useMenuVisibility hook which provides the visible state and setVisible function.

Check out the Sheet for more sheet behavior and styling.

import { AppBar } from "@react-md/core/app-bar/AppBar";
import { AppBarTitle } from "@react-md/core/app-bar/AppBarTitle";
import { Button } from "@react-md/core/button/Button";
import { DialogFooter } from "@react-md/core/dialog/DialogFooter";
import { Divider } from "@react-md/core/divider/Divider";
import { DropdownMenu } from "@react-md/core/menu/DropdownMenu";
import { MenuConfigurationProvider } from "@react-md/core/menu/MenuConfigurationProvider";
import { MenuItem } from "@react-md/core/menu/MenuItem";
import { useMenuVisibility } from "@react-md/core/menu/MenuVisibilityProvider";
import CloseIcon from "@react-md/material-icons/CloseIcon";
import { type ReactElement } from "react";

export default function SheetOptionsExample(): ReactElement {
  const renderAsSheet = true;

  return (
    <>
      <MenuConfigurationProvider
        sheetHeader={<Header />}
        sheetFooter={<Footer />}
        renderAsSheet={renderAsSheet}
        // these are the defaults
        sheetPosition="bottom"
        sheetVerticalSize="touch"
      >
        <DropdownMenu
          buttonChildren="Dropdown"
          // additional styles can be passed to the sheet from the DropdownMenu
          // sheetStyle={{ margin: "0 2rem" }}
          // sheetClassName="my-custom-class-name"
          //
          // any other props available on the Sheet component
          // sheetProps={{
          //   horizontalSize: "touch"
          // }}
        >
          <MenuItem disabled>Item 1</MenuItem>
          <MenuItem>Item 2</MenuItem>
          <DropdownMenu buttonChildren="Child Dropdown">
            <MenuItem>Item 1</MenuItem>
            <MenuItem>Item 2</MenuItem>
            <MenuItem>Item 3</MenuItem>
            <MenuItem>Item 4</MenuItem>
            <MenuItem>Item 5</MenuItem>
          </DropdownMenu>
          <MenuItem>Item 3</MenuItem>
          <MenuItem>Item 4</MenuItem>
          <MenuItem>Item 5</MenuItem>
        </DropdownMenu>
      </MenuConfigurationProvider>
    </>
  );
}

function Header(): ReactElement {
  const { setVisible } = useMenuVisibility();
  return (
    <AppBar theme="clear">
      <AppBarTitle>Custom</AppBarTitle>
      <Button
        onClick={() => {
          setVisible(false);
        }}
        buttonType="icon"
      >
        <CloseIcon />
      </Button>
    </AppBar>
  );
}

function Footer(): ReactElement {
  const { setVisible } = useMenuVisibility();
  return (
    <>
      <Divider />
      <DialogFooter>
        <Button
          onClick={() => {
            setVisible(false);
          }}
        >
          Cancel
        </Button>
      </DialogFooter>
    </>
  );
}

Press Enter to start editing.

Hoverable Menu

The DropdownMenu can be updated to become visible after a specific hover timeout by wrapping a group of dropdown menus within the MenuBar component to and providing a hoverTimeout prop. When the hoverTimeout is set to 0, the DropdownMenu will open immediately otherwise the DropdownMenu must be hovered for the duration in milliseconds.

The MenuBar also provides special keyboard movement behavior so the children should normally only be DropdownMenu components. Check out the MenuBar accessibility section for more info around the keyboard movement behavior.

import { DropdownMenu } from "@react-md/core/menu/DropdownMenu";
import { MenuBar } from "@react-md/core/menu/MenuBar";
import { MenuItem } from "@react-md/core/menu/MenuItem";
import type { ReactElement, ReactNode } from "react";

export default function HoverableMenuExample(): ReactElement {
  return (
    <MenuBar hoverTimeout={0}>
      <DropdownMenu buttonChildren="Item 1">
        <MenuItem>Menu Item 1</MenuItem>
        <MenuItem>Menu Item 2</MenuItem>
        <MenuItem>Menu Item 3</MenuItem>
        <MenuItem>Menu Item 4</MenuItem>
      </DropdownMenu>
      <DropdownMenu buttonChildren="Item 2">
        <MenuItem>Menu Item 1</MenuItem>
        <MenuItem>Menu Item 2</MenuItem>
        <MenuItem>Menu Item 3</MenuItem>
        <MenuItem>Menu Item 4</MenuItem>
      </DropdownMenu>
      <DropdownMenu buttonChildren="Item 3">
        <MenuItem>Menu Item 1</MenuItem>
        <MenuItem>Menu Item 2</MenuItem>
        <InfiniteDropdownMenu buttonChildren="Menu Item 3" depth={0} />
        <MenuItem>Menu Item 4</MenuItem>
      </DropdownMenu>
      <DropdownMenu buttonChildren="Item 4">
        <MenuItem>Menu Item 1</MenuItem>
        <MenuItem>Menu Item 2</MenuItem>
        <MenuItem>Menu Item 3</MenuItem>
        <MenuItem>Menu Item 4</MenuItem>
      </DropdownMenu>
    </MenuBar>
  );
}

interface InfiniteDropdownMenuProps {
  depth: number;
  buttonChildren: ReactNode;
}

function InfiniteDropdownMenu(props: InfiniteDropdownMenuProps): ReactElement {
  const { depth, buttonChildren } = props;
  return (
    <DropdownMenu buttonChildren={buttonChildren}>
      <MenuItem>Item 1</MenuItem>
      <MenuItem>Item 2</MenuItem>
      {Array.from({ length: 4 }, (_, i) => (
        <InfiniteDropdownMenu
          key={i}
          depth={depth + 1}
          buttonChildren={`Item ${i + 1}`}
        />
      ))}
      <MenuItem>Item 7</MenuItem>
    </DropdownMenu>
  );
}

Press Enter to start editing.

Click-first Hoverable Menu

The DropdownMenu can also behave like the browser bookmarks toolbar where once a menu has been opened, all other menus within the group will immediately open once hovered. Omit the hoverTimeout prop or set it to undefined to use this functionality.

import { DropdownMenu } from "@react-md/core/menu/DropdownMenu";
import { MenuBar } from "@react-md/core/menu/MenuBar";
import { MenuItem } from "@react-md/core/menu/MenuItem";
import type { ReactElement, ReactNode } from "react";

export default function ClickFirstHoverableMenu(): ReactElement {
  return (
    <MenuBar>
      <DropdownMenu buttonChildren="Item 1">
        <MenuItem>Menu Item 1</MenuItem>
        <MenuItem>Menu Item 2</MenuItem>
        <MenuItem>Menu Item 3</MenuItem>
        <MenuItem>Menu Item 4</MenuItem>
      </DropdownMenu>
      <DropdownMenu buttonChildren="Item 2">
        <MenuItem>Menu Item 1</MenuItem>
        <MenuItem>Menu Item 2</MenuItem>
        <MenuItem>Menu Item 3</MenuItem>
        <MenuItem>Menu Item 4</MenuItem>
      </DropdownMenu>
      <DropdownMenu buttonChildren="Item 3">
        <MenuItem>Menu Item 1</MenuItem>
        <MenuItem>Menu Item 2</MenuItem>
        <InfiniteDropdownMenu buttonChildren="Menu Item 3" depth={0} />
        <MenuItem>Menu Item 4</MenuItem>
      </DropdownMenu>
      <DropdownMenu buttonChildren="Item 4">
        <MenuItem>Menu Item 1</MenuItem>
        <MenuItem>Menu Item 2</MenuItem>
        <MenuItem>Menu Item 3</MenuItem>
        <MenuItem>Menu Item 4</MenuItem>
      </DropdownMenu>
    </MenuBar>
  );
}

interface InfiniteDropdownMenuProps {
  depth: number;
  buttonChildren: ReactNode;
}

function InfiniteDropdownMenu(props: InfiniteDropdownMenuProps): ReactElement {
  const { depth, buttonChildren } = props;
  return (
    <DropdownMenu buttonChildren={buttonChildren}>
      <MenuItem>Item 1</MenuItem>
      <MenuItem>Item 2</MenuItem>
      {Array.from({ length: 4 }, (_, i) => (
        <InfiniteDropdownMenu
          key={i}
          depth={depth + 1}
          buttonChildren={`Item ${i + 1}`}
        />
      ))}
      <MenuItem>Item 7</MenuItem>
    </DropdownMenu>
  );
}

Press Enter to start editing.

Additional MenuItem Components

The following components have been provided to merge the functionality and accessibility into a MenuItem.

These components will ensure the correct accessibility and keyboard movement within a DropdownMenu.

Enable the preventMenuHideOnClick if the menu should not close after clicking the menu item.

The MenuItemCheckbox component can be used to render a checkbox within a MenuItem and is a controlled component requiring the checked and onCheckedChange props. It is recommended to use the useCheckboxGroup hook when multiple checkboxes are involved since it also supports indeterminate checkboxes.

"use client";

import { useCheckboxGroup } from "@react-md/core/form/useCheckboxGroup";
import { DropdownMenu } from "@react-md/core/menu/DropdownMenu";
import { MenuItemCheckbox } from "@react-md/core/menu/MenuItemCheckbox";
import { MenuItemSeparator } from "@react-md/core/menu/MenuItemSeparator";
import { type ReactElement, useState } from "react";

const values = ["a", "b", "c", "d"] as const;
const labels = {
  a: "Label 1",
  b: "Label 2",
  c: "Label 3",
  d: "Label 4",
} as const;

export default function MenuItemCheckboxExample(): ReactElement {
  const [bold, setBold] = useState(false);
  const [italic, setItalic] = useState(false);
  const { getCheckboxProps, getIndeterminateProps } = useCheckboxGroup({
    values,
    menu: true,
  });

  return (
    <DropdownMenu buttonChildren="Checkboxes" themeType="outline">
      <MenuItemCheckbox {...getIndeterminateProps()} preventMenuHideOnClick>
        Toggle All
      </MenuItemCheckbox>
      {values.map((value) => (
        <MenuItemCheckbox
          key={value}
          {...getCheckboxProps(value)}
          preventMenuHideOnClick
        >
          {labels[value]}
        </MenuItemCheckbox>
      ))}
      <MenuItemSeparator />
      <MenuItemCheckbox disabled checked={false} onCheckedChange={() => {}}>
        Disabled
      </MenuItemCheckbox>
      <MenuItemCheckbox
        checked={bold}
        onCheckedChange={(checked) => {
          setBold(checked);
        }}
      >
        Bold
      </MenuItemCheckbox>
      <MenuItemCheckbox
        checked={italic}
        onCheckedChange={(checked) => {
          setItalic(checked);
        }}
      >
        Italic
      </MenuItemCheckbox>
    </DropdownMenu>
  );
}

Press Enter to start editing.

The MenuItemRadio component can be used to render a radio within a MenuItem and is a controlled component requiring the checked and onCheckedChange props. It is recommended to use the useRadioGroup hook to control the group of radios.

"use client";

import { type TextDecoration } from "@react-md/core/cssUtils";
import { useRadioGroup } from "@react-md/core/form/useRadioGroup";
import { DropdownMenu } from "@react-md/core/menu/DropdownMenu";
import { MenuItemRadio } from "@react-md/core/menu/MenuItemRadio";
import { Typography } from "@react-md/core/typography/Typography";
import { type ReactElement } from "react";

const decorations = [
  "none",
  "underline",
  "overline",
  "strike-through",
] as const;
type Decoration = (typeof decorations)[number];

const getDecoration = (decoration: Decoration): TextDecoration | undefined => {
  if (decoration === "none") {
    return undefined;
  }

  return decoration === "strike-through" ? "line-through" : decoration;
};

export default function MenuItemRadioExample(): ReactElement {
  const { getRadioProps } = useRadioGroup<Decoration>({
    menu: true,
    defaultValue: "none",
  });

  return (
    <DropdownMenu buttonChildren="Radio" themeType="outline">
      {decorations.map((decoration) => (
        <MenuItemRadio
          key={decoration}
          {...getRadioProps(decoration)}
          // preventMenuHideOnClick
        >
          <Typography textDecoration={getDecoration(decoration)} as="span">
            {decoration}
          </Typography>
        </MenuItemRadio>
      ))}
      <MenuItemRadio disabled checked={false} onCheckedChange={() => {}}>
        Disabled
      </MenuItemRadio>
    </DropdownMenu>
  );
}

Press Enter to start editing.

The MenuItemSwitch component can be used to render a Switch within a MenuItem and is a controlled component requiring the checked and onCheckedChange props. The state can be controlled by the useCheckboxGroup if desired.

"use client";

import { useCheckboxGroup } from "@react-md/core/form/useCheckboxGroup";
import { DropdownMenu } from "@react-md/core/menu/DropdownMenu";
import { MenuItemSwitch } from "@react-md/core/menu/MenuItemSwitch";
import { type ReactElement, useState } from "react";

export default function MenuItemSwitchExample(): ReactElement {
  const { getCheckboxProps } = useCheckboxGroup({ menu: true });
  const [checked, setChecked] = useState(false);

  return (
    <DropdownMenu buttonChildren="Dropdown" themeType="outline">
      <MenuItemSwitch
        checked={checked}
        onCheckedChange={(checked) => {
          setChecked(checked);
        }}
        preventMenuHideOnClick
      >
        Label
      </MenuItemSwitch>
      <MenuItemSwitch {...getCheckboxProps("0")}>Label</MenuItemSwitch>
      <MenuItemSwitch iconAfter {...getCheckboxProps("1")}>
        Label
      </MenuItemSwitch>
    </DropdownMenu>
  );
}

Press Enter to start editing.

The MenuItemTextField can be used to render a TextField within a MenuItem. This component will update the menu keyboard behavior so that the menu keyboard movement only occurs when there is no value within the text field.

Like the normal TextField, this component defaults to uncontrolled but can be controlled by providing the value and onChange props.

import { DropdownMenu } from "@react-md/core/menu/DropdownMenu";
import { MenuItem } from "@react-md/core/menu/MenuItem";
import { MenuItemSeparator } from "@react-md/core/menu/MenuItemSeparator";
import { MenuItemTextField } from "@react-md/core/menu/MenuItemTextField";
import SearchIcon from "@react-md/material-icons/SearchIcon";
import { type ReactElement } from "react";

export default function MenuItemTextFieldExample(): ReactElement {
  return (
    <DropdownMenu buttonChildren="Dropdown" themeType="outline">
      <MenuItemTextField
        aria-label="Search"
        placeholder="Search"
        rightAddon={<SearchIcon />}
      />
      <MenuItemSeparator />
      <MenuItem>Item 1</MenuItem>
      <MenuItem>Item 2</MenuItem>
      <MenuItem>Item 3</MenuItem>
      <MenuItem>Item 4</MenuItem>
    </DropdownMenu>
  );
}

Press Enter to start editing.

The MenuItemFileInput can be used to render FileInput components within a MenuItem. The main difference is that the upload icon will be rendered as a MenuItem addon and a label should be provided within the MenuItemFileInput children.

This component works with the useFileUpload hook and should be used for more complex file selection.

import { useFileUpload } from "@react-md/core/files/useFileUpload";
import { DropdownMenu } from "@react-md/core/menu/DropdownMenu";
import { MenuItemFileInput } from "@react-md/core/menu/MenuItemFileInput";
import { type ReactElement, useState } from "react";

const extensions = [
  "svg",
  "jpeg",
  "jpg",
  "png",
  "apng",
  "mkv",
  "mp4",
  "mpeg",
  "mpg",
  "webm",
  "mov",
] as const;

const FOUR_HUNDRED_MB = 400 * 1024 * 1024;
const maxFiles = 10;

export default function MenuItemFileInputExample(): ReactElement {
  const [file1, setFile1] = useState("");
  const [file2, setFile2] = useState("");

  // checkout the useFileUpload documentation for a real example with this hook
  const { accept, onChange } = useFileUpload({
    maxFiles,
    maxFileSize: FOUR_HUNDRED_MB,
    extensions,
  });
  return (
    <DropdownMenu buttonChildren="Options">
      <MenuItemFileInput
        onChange={(event) => {
          setFile1(event.currentTarget.value);
        }}
        secondaryText={`Selected file: ${file1 || "none"}`}
      >
        Upload
      </MenuItemFileInput>
      <MenuItemFileInput
        onChange={(event) => {
          setFile2(event.currentTarget.value);
        }}
        secondaryText={`Selected file: ${file2 || "none"}`}
        preventMenuHideOnClick
      >
        Upload
      </MenuItemFileInput>
      <MenuItemFileInput disabled onChange={() => {}}>
        Disabled
      </MenuItemFileInput>
      <MenuItemFileInput accept={accept} onChange={onChange} multiple>
        Upload Multiple
      </MenuItemFileInput>
    </DropdownMenu>
  );
}

Press Enter to start editing.

Use the MenuItemGroup and MenuItemSeparator when rendering the MenuItemRadio with other groups of MenuItems.

See more at https://www.w3.org/TR/wai-aria-1.1/#menuitemradio

import { useRadioGroup } from "@react-md/core/form/useRadioGroup";
import { DropdownMenu } from "@react-md/core/menu/DropdownMenu";
import { MenuItem } from "@react-md/core/menu/MenuItem";
import { MenuItemGroup } from "@react-md/core/menu/MenuItemGroup";
import { MenuItemRadio } from "@react-md/core/menu/MenuItemRadio";
import { MenuItemSeparator } from "@react-md/core/menu/MenuItemSeparator";
import { type ReactElement } from "react";

export default function MenuItemSeparatorAndGroupExample(): ReactElement {
  const { getRadioProps } = useRadioGroup({
    menu: true,
  });

  return (
    <DropdownMenu buttonChildren="Dropdown" themeType="outline">
      <MenuItemGroup aria-label="Example Radio Group">
        <MenuItemRadio {...getRadioProps("a")}>Radio 1</MenuItemRadio>
        <MenuItemRadio {...getRadioProps("b")}>Radio 2</MenuItemRadio>
        <MenuItemRadio {...getRadioProps("c")}>Radio 3</MenuItemRadio>
      </MenuItemGroup>
      <MenuItemSeparator />
      <MenuItem>Menu Item 1</MenuItem>
      <MenuItem>Menu Item 2</MenuItem>
      <MenuItem>Menu Item 3</MenuItem>
    </DropdownMenu>
  );
}

Press Enter to start editing.

Form MenuItem with Addons

The form menu item components can also render an addon like the MenuItem using addon, addonType, addonPosition props.

In addition, if the icon checkbox, radio, or switch should be rendered after the text instead of before, the iconAfter prop can be enabled.

import { Avatar } from "@react-md/core/avatar/Avatar";
import { useCheckboxGroup } from "@react-md/core/form/useCheckboxGroup";
import { DropdownMenu } from "@react-md/core/menu/DropdownMenu";
import { MenuItemCheckbox } from "@react-md/core/menu/MenuItemCheckbox";
import { MenuItemRadio } from "@react-md/core/menu/MenuItemRadio";
import { MenuItemSwitch } from "@react-md/core/menu/MenuItemSwitch";
import FavoriteIcon from "@react-md/material-icons/FavoriteIcon";
import { type ReactElement } from "react";

export default function FormMenuItemWithAddonsExample(): ReactElement {
  const { getCheckboxProps } = useCheckboxGroup({ menu: true });
  return (
    <DropdownMenu buttonChildren="Dropdown">
      <MenuItemCheckbox {...getCheckboxProps("a")} addon={<FavoriteIcon />}>
        With Icon
      </MenuItemCheckbox>
      <MenuItemRadio
        {...getCheckboxProps("b")}
        addon={<Avatar>A</Avatar>}
        addonType="avatar"
      >
        With Avatar
      </MenuItemRadio>
      <MenuItemSwitch
        {...getCheckboxProps("c")}
        addon={<img src="https://picsum.photos/56?image=700" alt="" />}
        addonType="media"
      >
        With Media
      </MenuItemSwitch>
      <MenuItemCheckbox
        {...getCheckboxProps("d")}
        addon={<img src="https://picsum.photos/100/56?image=800" alt="" />}
        addonType="large-media"
        iconAfter
      >
        With Large Media
      </MenuItemCheckbox>
    </DropdownMenu>
  );
}

Press Enter to start editing.

Since menus use the Portal to render in a different part of the DOM and position within the viewport using useFixedPositioning, menus can be rendered in dialogs without causing any issues like scrollbars appearing or not being able to view all the menu items.

"use client";

import { Button } from "@react-md/core/button/Button";
import { Dialog } from "@react-md/core/dialog/Dialog";
import { DialogContent } from "@react-md/core/dialog/DialogContent";
import { DropdownMenu } from "@react-md/core/menu/DropdownMenu";
import { MenuItem } from "@react-md/core/menu/MenuItem";
import { Typography } from "@react-md/core/typography/Typography";
import { useToggle } from "@react-md/core/useToggle";
import { type ReactElement } from "react";

export default function MenuWithinADialog(): ReactElement {
  const { toggled, enable, disable } = useToggle();
  return (
    <>
      <Button onClick={enable}>Show Dialog</Button>
      <Dialog aria-label="Example" visible={toggled} onRequestClose={disable}>
        <DialogContent>
          <Typography>
            {
              "Here is a paragraph of text. Even though it's really only two sentences."
            }
          </Typography>
          <DropdownMenu buttonChildren="Dropdown">
            <MenuItem>Item 1</MenuItem>
            <MenuItem>Item 2</MenuItem>
            <MenuItem>Item 3</MenuItem>
          </DropdownMenu>
        </DialogContent>
      </Dialog>
    </>
  );
}

Press Enter to start editing.

Accessibility

The menu components implement the menu and menu button specifications.

Keyboard Movement

The following keyboard movement has been implemented: