Headless UI
01 / 02

Menu, Dialog & Listbox

Menu, Dialog & Listbox

What "Headless" Means

Behavior, state, and accessibility (ARIA roles, keyboard nav, focus management) with zero built-in visual styling — you own every class/style. Built by the Tailwind team, pairs naturally with Tailwind utilities, but works with any CSS approach since it applies no styles itself.

Menu (Dropdown)

import { Menu } from '@headlessui/react'

function MyMenu() {
  return (
    <Menu>
      <Menu.Button className="btn">Options</Menu.Button>
      <Menu.Items className="dropdown-panel">
        <Menu.Item>
          {({ active }) => (
            <a className={active ? 'bg-blue-500' : ''} href="/edit">Edit</a>
          )}
        </Menu.Item>
        <Menu.Item>
          {({ active }) => (
            <a className={active ? 'bg-blue-500' : ''} href="/delete">Delete</a>
          )}
        </Menu.Item>
      </Menu.Items>
    </Menu>
  )
}

Menu/Menu.Button/Menu.Items/Menu.Item is a compound-component pattern. The render-prop { active } exposes internal state so you can style the highlighted item without managing that state yourself.

Dialog (Modal)

Handles focus trapping while open, Escape/outside-click to close, and restoring focus to the trigger element on close — the subtle accessibility behaviors easy to get wrong hand-rolling a modal.

Listbox — Stylable Select

Native <select> is accessible but can't be deeply restyled cross-browser. Listbox reimplements the same accessible pattern (same keyboard behavior as a native select) while being fully custom-stylable.

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free