Skip to Content

Dropdown menu

The dropdown menu displays a list of selectable choices and options.

import * as React from 'react';
import {
  Text,
  MenuExtraContent,
  MenuDivider,
  MenuItem,
  DropdownMenu,
  IconButton,
} from '@itwin/itwinui-react';
import { SvgMore } from '@itwin/itwinui-icons-react';

export default () => {
  const dropdownMenuItems = (close) => [
    <MenuExtraContent key={0}>
      <>
        <Text variant='leading'>Terry Rivers</Text>
        <Text isMuted>terry.rivers@email.com</Text>
      </>
    </MenuExtraContent>,
    <MenuDivider key={1} />,
    <MenuItem key={2} onClick={() => close()}>
      View profile
    </MenuItem>,
    <MenuItem key={3} onClick={() => close()}>
      Sign out
    </MenuItem>,
  ];

  return (
    <>
      <DropdownMenu menuItems={dropdownMenuItems}>
        <IconButton aria-label='More options'>
          <SvgMore />
        </IconButton>
      </DropdownMenu>
    </>
  );
};

A dropdown menu is a control element meant to allow the user to select an option from a list. This is a list of options contained within a collapsible container and are mostly found in forms and dialogs. When it is clicked, it expands into a full list of items. This list can be scrollable if there are a lot of options, to avoid covering up too much of the interface. It collapses back into a single line when the user either selects an option in the list, or clicks outside of it.

Variants

Basic

The basic dropdown menu displays a list of clickable options when expanded. Additionally, each option can be disabled to disallow clicking.

import * as React from 'react';
import { MenuItem, DropdownMenu, IconButton } from '@itwin/itwinui-react';
import { SvgMore } from '@itwin/itwinui-icons-react';

export default () => {
  const dropdownMenuItems = (close) => [
    <MenuItem key={1} onClick={() => close()}>
      Item #1
    </MenuItem>,
    <MenuItem key={2} onClick={() => close()}>
      Item #2
    </MenuItem>,
    <MenuItem key={3} onClick={() => close()} disabled>
      Item #3
    </MenuItem>,
  ];

  return (
    <>
      <DropdownMenu menuItems={dropdownMenuItems}>
        <IconButton aria-label='More options'>
          <SvgMore />
        </IconButton>
      </DropdownMenu>
    </>
  );
};

Start icon

An icon may be display on the left side of option for better visualization of common menu items.

import * as React from 'react';
import { MenuItem, DropdownMenu, IconButton } from '@itwin/itwinui-react';
import {
  SvgMore,
  SvgCrop,
  SvgClipboard,
  SvgMove,
} from '@itwin/itwinui-icons-react';

export default () => {
  const dropdownMenuItems = (close) => [
    <MenuItem key={1} onClick={() => close()} startIcon={<SvgCrop />}>
      Crop
    </MenuItem>,
    <MenuItem key={2} onClick={() => close()} startIcon={<SvgClipboard />}>
      Paste
    </MenuItem>,
    <MenuItem key={3} onClick={() => close()} startIcon={<SvgMove />}>
      Move
    </MenuItem>,
  ];

  return (
    <>
      <DropdownMenu menuItems={dropdownMenuItems}>
        <IconButton aria-label='More options'>
          <SvgMore />
        </IconButton>
      </DropdownMenu>
    </>
  );
};

End icon

End icon goes on the right side of option.

import * as React from 'react';
import { MenuItem, DropdownMenu, IconButton } from '@itwin/itwinui-react';
import {
  SvgMore,
  SvgCrop,
  SvgClipboard,
  SvgMove,
} from '@itwin/itwinui-icons-react';

export default () => {
  const dropdownMenuItems = (close) => [
    <MenuItem key={1} onClick={() => close()} endIcon={<SvgCrop />}>
      Crop
    </MenuItem>,
    <MenuItem key={2} onClick={() => close()} endIcon={<SvgClipboard />}>
      Paste
    </MenuItem>,
    <MenuItem key={3} onClick={() => close()} endIcon={<SvgMove />}>
      Move
    </MenuItem>,
  ];

  return (
    <>
      <DropdownMenu menuItems={dropdownMenuItems}>
        <IconButton aria-label='More options'>
          <SvgMore />
        </IconButton>
      </DropdownMenu>
    </>
  );
};

Sublabel

The dropdown menu items can also be displayed on two lines, with a label and a sublabel for improved clarity.

import * as React from 'react';
import { MenuItem, DropdownMenu, IconButton } from '@itwin/itwinui-react';
import { SvgMore, SvgPlaceholder } from '@itwin/itwinui-icons-react';

export default () => {
  const dropdownMenuItems = (close) => [
    <MenuItem
      key={1}
      onClick={() => close()}
      startIcon={<SvgPlaceholder />}
      sublabel='Sublabel #1'
    >
      Item #1
    </MenuItem>,
    <MenuItem
      key={2}
      onClick={() => close()}
      startIcon={<SvgPlaceholder />}
      sublabel='Sublabel #2'
    >
      Item #2
    </MenuItem>,
    <MenuItem
      key={3}
      onClick={() => close()}
      startIcon={<SvgPlaceholder />}
      sublabel='Sublabel #3'
    >
      Item #3
    </MenuItem>,
  ];

  return (
    <>
      <DropdownMenu menuItems={dropdownMenuItems}>
        <IconButton aria-label='More options'>
          <SvgMore />
        </IconButton>
      </DropdownMenu>
    </>
  );
};

The menu can contain one or more sub-menus. To show that an option within the list has another menu with more options associated to it, there is usually a caret icon pointing outward. Hovering on that option will expand the sub-menu outside the parent menu. Ideally, the list of child items should appear to the right of the parent item.

import * as React from 'react';
import { MenuItem, DropdownMenu, IconButton } from '@itwin/itwinui-react';
import { SvgMore } from '@itwin/itwinui-icons-react';

export default () => {
  const dropdownMenuItems = (close) => [
    <MenuItem key={1} onClick={() => close()}>
      Item #1
    </MenuItem>,
    <MenuItem key={2} onClick={() => close()}>
      Item #2
    </MenuItem>,
    <MenuItem
      key={3}
      subMenuItems={[
        <MenuItem
          key={4}
          subMenuItems={[
            <MenuItem key={7} onClick={() => close()}>
              Item #7
            </MenuItem>,
            <MenuItem key={8} onClick={() => close()}>
              Item #8
            </MenuItem>,
          ]}
        >
          Item #4
        </MenuItem>,
        <MenuItem key={5} onClick={() => close()}>
          Item #5
        </MenuItem>,
        <MenuItem
          key={6}
          subMenuItems={[
            <MenuItem key={9} onClick={() => close()}>
              Item #9
            </MenuItem>,
            <MenuItem key={10} onClick={() => close()}>
              Item #10
            </MenuItem>,
          ]}
        >
          Item #6
        </MenuItem>,
      ]}
    >
      Item #3
    </MenuItem>,
  ];

  return (
    <>
      <DropdownMenu menuItems={dropdownMenuItems}>
        <IconButton aria-label='More options'>
          <SvgMore />
        </IconButton>
      </DropdownMenu>
    </>
  );
};

Separator

The separator line helps to visually organize the options into distinct categories, making it easier for the user to locate and select the desired option.

import * as React from 'react';
import {
  MenuItem,
  DropdownMenu,
  IconButton,
  MenuDivider,
} from '@itwin/itwinui-react';
import { SvgMore } from '@itwin/itwinui-icons-react';

export default () => {
  const dropdownMenuItems = (close) => [
    <MenuItem key={1} onClick={() => close()}>
      Item #1
    </MenuItem>,
    <MenuItem key={2} onClick={() => close()}>
      Item #2
    </MenuItem>,
    <MenuDivider key={3} />,
    <MenuItem key={4} onClick={() => close()} disabled>
      Item #3
    </MenuItem>,
    <MenuItem key={5} onClick={() => close()}>
      Item #4
    </MenuItem>,
  ];

  return (
    <>
      <DropdownMenu menuItems={dropdownMenuItems}>
        <IconButton aria-label='More options'>
          <SvgMore />
        </IconButton>
      </DropdownMenu>
    </>
  );
};

Content

The menu can contain extra content, including both text and additional selectable dropdown items, which allows for additional nested options to be displayed. This type of dropdown menu is commonly used in applications that have multiple levels of options or actions, such as in navigation or switching user’s context.

import * as React from 'react';
import {
  Text,
  MenuExtraContent,
  MenuDivider,
  MenuItem,
  DropdownMenu,
  IconButton,
  Select,
} from '@itwin/itwinui-react';
import { SvgMore } from '@itwin/itwinui-icons-react';

export default () => {
  const [userType, setUserType] = React.useState('User');
  const dropdownMenuItems = (close) => [
    <MenuExtraContent key={0}>
      <>
        <Text variant='leading'>Terry Rivers</Text>
        <Text isMuted>terry.rivers@email.com</Text>
        <Select
          options={[
            { value: 'User', label: 'User' },
            { value: 'Moderator', label: 'Moderator' },
            { value: 'Administrator', label: 'Administrator' },
          ]}
          value={userType}
          onChange={(type) => setUserType(type)}
        />
      </>
    </MenuExtraContent>,
    <MenuDivider key={1} />,
    <MenuItem key={2} onClick={() => close()}>
      View profile
    </MenuItem>,
    <MenuItem key={3} onClick={() => close()}>
      Sign out
    </MenuItem>,
  ];

  return (
    <>
      <DropdownMenu menuItems={dropdownMenuItems}>
        <IconButton aria-label='More options'>
          <SvgMore />
        </IconButton>
      </DropdownMenu>
    </>
  );
};

hide middleware

By default, the menu is hidden when the trigger is hidden (e.g. out of the scrollport/viewport). This is useful when the trigger element is scrolled out of view.

import * as React from 'react';
import {
  Surface,
  List,
  ListItem,
  DropdownMenu,
  IconButton,
  MenuItem,
} from '@itwin/itwinui-react';
import { SvgMore } from '@itwin/itwinui-icons-react';

export default () => {
  const dropdownMenuItems = (close) => [
    <MenuItem key={1} onClick={() => close()}>
      Option #1
    </MenuItem>,
    <MenuItem key={2} onClick={() => close()}>
      Option #2
    </MenuItem>,
    <MenuItem key={3} onClick={() => close()} disabled>
      Option #3
    </MenuItem>,
  ];

  const items = new Array(30).fill(0);

  return (
    <Surface className='demo-container'>
      <Surface.Body as={List} className='list'>
        {items.map((_, i) => (
          <ListItem key={i}>
            <ListItem.Content>Item {i}</ListItem.Content>
            <DropdownMenu menuItems={dropdownMenuItems}>
              <IconButton
                styleType='borderless'
                label='More options'
                size='small'
              >
                <SvgMore />
              </IconButton>
            </DropdownMenu>
          </ListItem>
        ))}
      </Surface.Body>
    </Surface>
  );
};

If the menu gets hidden even when it shouldn’t (e.g. due to some custom styles interfering with the trigger’s hide detection), consider disabling the hide middleware.

<DropdownMenu
middleware={{
hide: false,
}}
>
</DropdownMenu>

Props

Prop Description Default
menuItems
List of menu items. Recommended to use MenuItem component. You can pass function that takes argument close that closes the dropdown menu, or a list of MenuItems.
((close: () => void) => Element[]) | Element | Element[]
role
ARIA role. Role of menu. For menu use 'menu', for select use 'listbox'.
string
'menu'
children
Child element to wrap dropdown with.
ReactNode
middleware
Middleware options.
By default, hide is enabled. If the menu gets hidden even when it shouldn't (e.g. some custom styles interfering with the trigger's hide detection) consider disabling the hide middleware. @see https://floating-ui.com/docs/middleware
{ hide?: boolean; }
placement
Placement of the popover content.
Placement
'bottom-start'
visible
Controlled flag for whether the popover is visible.
boolean
onVisibleChange
Callback invoked every time the popover visibility changes as a result of internal logic. Should be used alongside visible prop.
(visible: boolean) => void
matchWidth
Whether the popover should match the width of the trigger.
boolean
portal
Where should the element be portaled to?
If true, it will portal into nearest ThemeProvider's portalContainer.
If false, it will not be portaled.
Otherwise, it will portal to the element passed to to.
If to/to() === null/undefined, the default behavior will be used (i.e. as if portal is not passed).
boolean | { to: HTMLElement | (() => HTMLElement); }
true
as
"symbol" | "object" | "div" | "a" | "abbr" | "address" | "area" | "article" | "aside" | "audio" | "b" | "base" | "bdi" | "bdo" | "big" | "blockquote" | "body" | "br" | "button" | "canvas" | ... 158 more ... | FunctionComponent<...>