Dropdown

Documentation for the <Menu /> dropdown component. Learn about the available props and their use.

Installation

Menu (Dropdown) is part of a set of unstyled and fully accessible components created by Headless UI.
See the Headless UI documentation to learn more.

To get started, install Headless UI via yarn.

# Yarn
yarn add @headlessui/react

Basic Example

Dropdowns display a list of choices and allow users to make a selection from multiple options.

According to Headless UI, Menu Buttons are built using the Menu, Menu.Button, Menu.Items, and Menu.Item components.
The Menu.Button automatically opens/closes the Menu.Items when clicked. The list of items receives focus and is navigable via the keyboard when the menu is open.

import { Menu } from "@headlessui/react";
function IndexPage() {
return (
<main>
{/* ... */}
<Menu>
<Menu.Button>More</Menu.Button>
<Menu.Items>
<Menu.Item>
{({ active }) => (
<a
className={`${active && "bg-surface-300"}`}
href="#"
>
Account settings
</a>
)}
</Menu.Item>
{/* ... */}
<Menu.Item disabled>
<span className="opacity-75">Invite a friend (coming soon!)</span>
</Menu.Item>
</Menu.Items>
</Menu>
{/* ... */}
</main>
)
}

The previous example describes the simplest way of using the Menu (Dropdown) component, it uses default props and renders a component like the following:

Dropdown menu button

Menu (Dropdown) component implements the Menu.Button child component, which can be customized in several styles.
There are three main styles; Light, Outline and Dark. Each one comes with two variants; icon on and off.

Light styling

Light style, as its name suggests, it's appropriate to use in light mode.

Icon off

"Icon off variant" means that no icon is added to the left side of the Menu.Button content.

import { Menu } from "@headlessui/react";
function IndexPage() {
const menuItemCommonClasses = "flex items-center w-full p-4 rounded font-rubik text-base text-surface-600 leading-5";
return (
<main>
{/* ... */}
<Menu as="div" className="relative inline-block text-left">
{({ open }) => (
<>
<Menu.Button
className={cn(
"inline-flex justify-between items-center w-full h-10 px-3 py-2",
"font-montserrat font-bold text-base leading-6 border border-transparent rounded",
"focus:bg-surface-800 focus:border-primary-500 focus:ring-2 focus:ring-primary-200", {
["text-white bg-surface-600 hover:bg-surface-800"]: !open,
["text-surface-600 bg-surface-200 border-primary-500 ring-2 ring-primary-200 hover:bg-surface-200"]: open
}
)}
>
<span className="mr-3">Label</span>
<CaretDownIcon className="w-5 h-5" />
</Menu.Button>
<Menu.Items
className="absolute left-0 w-36 mt-2 origin-top-left bg-white rounded shadow-sm z-10 focus:outline-none"
>
<Menu.Item>
{({ active }) => (
<button
className={cn(menuItemCommonClasses, { ["bg-surface-50"]: active })}
>
Item
</button>
)}
</Menu.Item>
{/* Menu items here ... */}
</Menu.Items>
</>
)}
</Menu>
{/* ... */}
</main>
)
}
Icon on

"Icon on variant" means that an icon is added to the left side of the Menu.Button content.

import { Menu } from "@headlessui/react";
function IndexPage() {
const menuItemCommonClasses = "flex items-center w-full p-4 rounded font-rubik text-base text-surface-600 leading-5";
return (
<main>
{/* ... */}
<Menu as="div" className="relative inline-block text-left">
{({ open }) => (
<>
<Menu.Button
className={cn(
"inline-flex justify-between items-center w-full h-10 px-3 py-2",
"font-montserrat font-bold text-base leading-6 border border-transparent rounded",
"focus:bg-surface-800 focus:border-primary-500 focus:ring-2 focus:ring-primary-200", {
["text-white bg-surface-600 hover:bg-surface-800"]: !open,
["text-surface-600 bg-surface-200 border-primary-500 ring-2 ring-primary-200 hover:bg-surface-200"]: open
}
)}
>
<div className="flex items-center w-full mr-3">
<AdvancedLevelIcon className="w-5 h-5 mr-2" />
<span>Label</span>
</div>
<CaretDownIcon className="w-5 h-5" />
</Menu.Button>
<Menu.Items
className="absolute left-0 w-36 mt-2 origin-top-left bg-white rounded shadow-sm z-10 focus:outline-none"
>
<Menu.Item>
{({ active }) => (
<button
className={cn(menuItemCommonClasses, { ["bg-surface-50"]: active })}
>
Item
</button>
)}
</Menu.Item>
{/* Menu items here ... */}
</Menu.Items>
</>
)}
</Menu>
{/* ... */}
</main>
)
}

Outline styling

At first sight, outline styles defines a Menu (Dropdown) with a Menu.Button without background, but its border highlighted.

Icon off
import { Menu } from "@headlessui/react";
function IndexPage() {
const menuItemCommonClasses = "flex items-center w-full p-4 rounded font-rubik text-base text-surface-600 leading-5";
return (
<main>
{/* ... */}
<Menu as="div" className="relative inline-block text-left">
{({ open }) => (
<>
<Menu.Button
className={cn(
"inline-flex justify-between items-center w-full h-10 px-3 py-2",
"font-montserrat font-bold text-base leading-6 border-2 border-surface-500 rounded",
"hover:text-white hover:bg-surface-500 focus:bg-surface-500 focus:text-white focus:border",
"focus:border-primary-500 focus:ring-2 focus:ring-primary-200", {
["text-surface-600 bg-transparent"]: !open,
["text-white bg-surface-500 border-primary-500 ring-2 ring-primary-200"]: open
}
)}
>
<span className="mr-3">Label</span>
<CaretDownIcon className="w-5 h-5" />
</Menu.Button>
<Menu.Items
className="absolute left-0 w-36 mt-2 origin-top-left bg-white rounded shadow-sm z-10 focus:outline-none"
>
<Menu.Item>
{({ active }) => (
<button
className={cn(menuItemCommonClasses, { ["bg-surface-50"]: active })}
>
Item
</button>
)}
</Menu.Item>
{/* Menu items here ... */}
</Menu.Items>
</>
)}
</Menu>
{/* ... */}
</main>
)
}
Icon on
import { Menu } from "@headlessui/react";
function IndexPage() {
const menuItemCommonClasses = "flex items-center w-full p-4 rounded font-rubik text-base text-surface-600 leading-5";
return (
<main>
{/* ... */}
<Menu as="div" className="relative inline-block text-left">
{({ open }) => (
<>
<Menu.Button
className={cn(
"inline-flex justify-between items-center w-full h-10 px-3 py-2",
"font-montserrat font-bold text-base leading-6 border-2 border-surface-500 rounded",
"hover:text-white hover:bg-surface-500 focus:bg-surface-500 focus:text-white",
"focus:border focus:border-primary-500 focus:ring-2 focus:ring-primary-200", {
["text-surface-600 bg-transparent"]: !open,
["text-white bg-surface-500 border-primary-500 ring-2 ring-primary-200"]: open
}
)}
>
<div className="flex items-center w-full mr-3">
<AdvancedLevelIcon className="w-5 h-5 mr-2" />
<span>Label</span>
</div>
<CaretDownIcon className="w-5 h-5" />
</Menu.Button>
<Menu.Items
className="absolute left-0 w-36 mt-2 origin-top-left bg-white rounded shadow-sm z-10 focus:outline-none"
>
<Menu.Item>
{({ active }) => (
<button
className={cn(menuItemCommonClasses, { ["bg-surface-50"]: active})}
>
Item
</button>
)}
</Menu.Item>
{/* Menu items here ... */}
</Menu.Items>
</>
)}
</Menu>
{/* ... */}
</main>
)
}

Dark styling

Dark style implements all the light styles with a plus, when the current page's theme is changed to dark mode, the style of the Menu (Dropdown) adjusts to it accordingly.

Icon off

A user can change the theme to dark mode and Menu.Button styles will change too.

import { Menu } from "@headlessui/react";
function IndexPage() {
const menuItemCommonClasses = "flex items-center w-full p-4 rounded font-rubik text-base text-surface-600 leading-5";
return (
<main>
{/* ... */}
<Menu as="div" className="relative inline-block text-left">
{({ open }) => (
<>
<Menu.Button
className={cn(
"inline-flex justify-between items-center w-full h-10 px-3 py-2 font-montserrat font-bold text-base leading-6 border border-transparent",
"rounded focus:bg-surface-800 focus:border-primary-500 focus:ring-2 focus:ring-primary-200 dark:focus:bg-surface-200", {
["text-white bg-surface-600 hover:bg-surface-800 dark:text-surface-600 dark:bg-surface-100 dark:hover:bg-surface-200"]: !open,
["text-surface-600 bg-surface-200 border-primary-500 ring-2 ring-primary-200 dark:bg-surface-200 hover:bg-surface-200"]: open
}
)}
>
<span className="mr-3">Label</span>
<CaretDownIcon className="w-5 h-5" />
</Menu.Button>
<Menu.Items
className="absolute left-0 w-36 mt-2 origin-top-left bg-white rounded shadow-sm z-10 focus:outline-none"
>
<Menu.Item>
{({ active }) => (
<button
className={cn(menuItemCommonClasses, { ["bg-surface-50"]: active })}
>
Item
</button>
)}
</Menu.Item>
{/* Menu items here ... */}
</Menu.Items>
</>
)}
</Menu>
{/* ... */}
</main>
)
}
Icon on

A user can change the theme to dark mode and Menu.Button styles will change too, this variant includes an icon at the left side of the Menu.Button content.

import { Menu } from "@headlessui/react";
function IndexPage() {
const menuItemCommonClasses = "flex items-center w-full p-4 rounded font-rubik text-base text-surface-600 leading-5";
return (
<main>
{/* ... */}
<Menu as="div" className="relative inline-block text-left">
{({ open }) => (
<>
<Menu.Button
className={cn(
"inline-flex justify-between items-center w-full h-10 px-3 py-2 font-montserrat font-bold text-base leading-6 border border-transparent",
"rounded focus:bg-surface-800 focus:border-primary-500 focus:ring-2 focus:ring-primary-200 dark:focus:bg-surface-200", {
["text-white bg-surface-600 hover:bg-surface-800 dark:text-surface-600 dark:bg-surface-100 dark:hover:bg-surface-200"]: !open,
["text-surface-600 bg-surface-200 border-primary-500 ring-2 ring-primary-200 dark:bg-surface-200 hover:bg-surface-200"]: open
}
)}
>
<div className="flex items-center w-full mr-3">
<AdvancedLevelIcon className="w-5 h-5 mr-2" />
<span>Label</span>
</div>
<CaretDownIcon className="w-5 h-5" />
</Menu.Button>
<Menu.Items
className="absolute left-0 w-36 mt-2 origin-top-left bg-white rounded shadow-sm z-10 focus:outline-none"
>
<Menu.Item>
{({ active }) => (
<button
className={cn(menuItemCommonClasses, { ["bg-surface-50"]: active })}
>
Item
</button>
)}
</Menu.Item>
{/* Menu items here ... */}
</Menu.Items>
</>
)}
</Menu>
{/* ... */}
</main>
)
}

Dropdown menu items

Menu (Dropdown) implements the Menu.Items and Menu.Item child components, which can be fully customized. The following examples describe three different styles for the dropdown items.

Text only styling

Text only menu items render text in the corresponding place for each menu item an nothing else.

import { Menu } from "@headlessui/react";
function IndexPage() {
const menuItemCommonClasses = "flex items-center w-full p-4 rounded font-rubik text-base text-surface-600 leading-5";
return (
<main>
{/* ... */}
<Menu as="div" className="relative inline-block text-left">
{({ open }) => (
<>
<Menu.Button
className={/* classes here */}
>
<span className="mr-3">Label</span>
<CaretDownIcon className="w-5 h-5" />
</Menu.Button>
<Menu.Items
className={/* classes here */}
>
<Menu.Item>
{({ active }) => (
<button
className={cn(menuItemCommonClasses, { ["bg-surface-50"]: active })}
>
Item
</button>
)}
</Menu.Item>
{/* Menu items here ... */}
</Menu.Items>
</>
)}
</Menu>
{/* ... */}
</main>
)
}

Icon lead styling

Icon lead menu items render an icon next to the left side of the menu item text content.

import { Menu } from "@headlessui/react";
function IndexPage() {
const menuItemCommonClasses = "flex items-center w-full p-4 rounded font-rubik text-base text-surface-600 leading-5";
return (
<main>
{/* ... */}
<Menu as="div" className="relative inline-block text-left">
{({ open }) => (
<>
<Menu.Button
className={/* classes here */}
>
<span className="mr-3">Label</span>
<CaretDownIcon className="w-5 h-5" />
</Menu.Button>
<Menu.Items
className={/* classes here */}
>
<Menu.Item>
{({ active }) => (
<button
className={cn(menuItemCommonClasses, { ["bg-surface-50"]: active })}
>
<EditIcon className="w-4 h-4 mr-2" />
Item
</button>
)}
</Menu.Item>
{/* Menu items here ... */}
</Menu.Items>
</>
)}
</Menu>
{/* ... */}
</main>
)
}

With selection styling

Selection menu items render a check icon next to the right side of the menu item text content and it's displayed only when the menu item is active.

import { Menu } from "@headlessui/react";
function IndexPage() {
const menuItemCommonClasses = "flex items-center w-full p-4 rounded font-rubik text-base text-surface-600 leading-5";
return (
<main>
{/* ... */}
<Menu as="div" className="relative inline-block text-left">
{({ open }) => (
<>
<Menu.Button
className={/* classes here */}
>
<span className="mr-3">Label</span>
<CaretDownIcon className="w-5 h-5" />
</Menu.Button>
<Menu.Items
className={/* classes here */}
>
<Menu.Item>
{({ active }) => (
<button
className={cn(
"justify-between",
menuItemCommonClasses, {
["bg-surface-50"]: active,
}
)}
>
<div className="flex items-center">
<EditIcon className="w-4 h-4 mr-2" />
Item
</div>
{
active &&
<CheckIcon className="text-primary-500 w-4 h-4" />
}
</button>
)}
</Menu.Item>
{/* Menu items here ... */}
</Menu.Items>
</>
)}
</Menu>
{/* ... */}
</main>
)
}

Disabled

Menu (Dropdown) can also be customized to be disabled; either the whole interaction by adding the disable prop in the Menu.Button component or by disabling menu items individually with the disabled prop in the Menu.Item component.

import { Menu } from "@headlessui/react";
function IndexPage() {
const menuItemCommonClasses = "flex items-center w-full p-4 rounded font-rubik text-base text-surface-600 leading-5";
const disabled = true;
return (
<main>
{/* ... */}
<Menu as="div" className="relative inline-block text-left">
{({ open }) => (
<>
<Menu.Button
disabled={disabled}
className={/* classes here */}
>
<span className="mr-3">Label</span>
<CaretDownIcon className="w-5 h-5" />
</Menu.Button>
<Menu.Items
className={/* classes here */}
>
<Menu.Item disabled>
{({ active }) => (
<button
className={/* classes here */}
>
Item
</button>
)}
</Menu.Item>
{/* Menu items here ... */}
</Menu.Items>
</>
)}
</Menu>
{/* ... */}
</main>
)
}

To customize and learn about the Menu (Dropdown) component see the Headless UI documentation.