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>
</>
);
};
Submenu
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.
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<...> |