Listbox
A painstakingly re-engineered select menu, just so you can put a flag in it or have a placeholder.
import { Field, Label } from '@/components/fieldset'
import { Listbox, ListboxLabel, ListboxOption } from '@/components/listbox'
function Example() {
  return (
    <Field>
      <Label>Project status</Label>
      <Listbox name="status" defaultValue="active">
        <ListboxOption value="active">
          <ListboxLabel>Active</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="paused">
          <ListboxLabel>Paused</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="delayed">
          <ListboxLabel>Delayed</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="canceled">
          <ListboxLabel>Canceled</ListboxLabel>
        </ListboxOption>
      </Listbox>
    </Field>
  )
}
import { Field, Label } from '@/components/fieldset'
import { Listbox, ListboxLabel, ListboxOption } from '@/components/listbox'
function Example() {
  return (
    <Field>
      <Label>Project status</Label>
      <Listbox name="status" defaultValue="active">
        <ListboxOption value="active">
          <ListboxLabel>Active</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="paused">
          <ListboxLabel>Paused</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="delayed">
          <ListboxLabel>Delayed</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="canceled">
          <ListboxLabel>Canceled</ListboxLabel>
        </ListboxOption>
      </Listbox>
    </Field>
  )
}
Component API
| Prop | Default | Description | 
|---|---|---|
Listbox extends the Headless UI <Listbox> component | ||
disabled | false | Whether or not to disable the listbox. | 
invalid | false | Whether or not the listbox has a validation error. | 
name | - | The name to use when submitting an HTML form. | 
defaultValue | - | The initial value for the listbox. | 
value | - | The controlled value of the listbox. | 
onChange | - | Handler to call when the listbox value changes. | 
placeholder | - | The text to show when no option is selected. | 
ListboxOption extends the Headless UI <ListboxOption> component | ||
value | - | The option value. | 
ListboxLabel extends the JSX <span> element | ||
| This component does not expose any component-specific props. | ||
ListboxDescription extends the JSX <span> element | ||
| This component does not expose any component-specific props. | ||
Field extends the Headless UI <Field> component | ||
disabled | false | Whether or not to disable the entire field. | 
Label extends the Headless UI <Label> component | ||
| This component does not expose any component-specific props. | ||
Description extends the Headless UI <Description> component | ||
| This component does not expose any component-specific props. | ||
ErrorMessage extends the Headless UI <Description> component | ||
| This component does not expose any component-specific props. | ||
Examples
Basic example
Use the Listbox, ListboxOption, and ListboxLabel components to build a basic listbox:
import { Listbox, ListboxLabel, ListboxOption } from '@/components/listbox'
function Example() {
  return (
    <Listbox name="status" defaultValue="active">
      <ListboxOption value="active">
        <ListboxLabel>Active</ListboxLabel>
      </ListboxOption>
      <ListboxOption value="paused">
        <ListboxLabel>Paused</ListboxLabel>
      </ListboxOption>
      <ListboxOption value="delayed">
        <ListboxLabel>Delayed</ListboxLabel>
      </ListboxOption>
      <ListboxOption value="canceled">
        <ListboxLabel>Canceled</ListboxLabel>
      </ListboxOption>
    </Listbox>
  )
}
import { Listbox, ListboxLabel, ListboxOption } from '@/components/listbox'
function Example() {
  return (
    <Listbox name="status" defaultValue="active">
      <ListboxOption value="active">
        <ListboxLabel>Active</ListboxLabel>
      </ListboxOption>
      <ListboxOption value="paused">
        <ListboxLabel>Paused</ListboxLabel>
      </ListboxOption>
      <ListboxOption value="delayed">
        <ListboxLabel>Delayed</ListboxLabel>
      </ListboxOption>
      <ListboxOption value="canceled">
        <ListboxLabel>Canceled</ListboxLabel>
      </ListboxOption>
    </Listbox>
  )
}
Make sure to provide an aria-label for assistive technology, or connect the Listbox to your own <label> element
using an id.
With label
Wrap a Label and Listbox with the Field component to automatically associate them using a generated ID:
import { Field, Label } from '@/components/fieldset'
import { Listbox, ListboxLabel, ListboxOption } from '@/components/listbox'
function Example() {
  return (
    <Field>
      <Label>Project status</Label>
      <Listbox name="status" defaultValue="active">
        <ListboxOption value="active">
          <ListboxLabel>Active</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="paused">
          <ListboxLabel>Paused</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="delayed">
          <ListboxLabel>Delayed</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="canceled">
          <ListboxLabel>Canceled</ListboxLabel>
        </ListboxOption>
      </Listbox>
    </Field>
  )
}
import { Field, Label } from '@/components/fieldset'
import { Listbox, ListboxLabel, ListboxOption } from '@/components/listbox'
function Example() {
  return (
    <Field>
      <Label>Project status</Label>
      <Listbox name="status" defaultValue="active">
        <ListboxOption value="active">
          <ListboxLabel>Active</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="paused">
          <ListboxLabel>Paused</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="delayed">
          <ListboxLabel>Delayed</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="canceled">
          <ListboxLabel>Canceled</ListboxLabel>
        </ListboxOption>
      </Listbox>
    </Field>
  )
}
With description
Use the Description component to add a description above or below your Listbox:
This will be visible to clients on the project.
import { Description, Field, Label } from '@/components/fieldset'
import { Listbox, ListboxLabel, ListboxOption } from '@/components/listbox'
function Example() {
  return (
    <Field>
      <Label>Project status</Label>
      <Description>This will be visible to clients on the project.</Description>
      <Listbox name="status" defaultValue="active">
        <ListboxOption value="active">
          <ListboxLabel>Active</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="paused">
          <ListboxLabel>Paused</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="delayed">
          <ListboxLabel>Delayed</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="canceled">
          <ListboxLabel>Canceled</ListboxLabel>
        </ListboxOption>
      </Listbox>
    </Field>
  )
}
import { Description, Field, Label } from '@/components/fieldset'
import { Listbox, ListboxLabel, ListboxOption } from '@/components/listbox'
function Example() {
  return (
    <Field>
      <Label>Project status</Label>
      <Description>This will be visible to clients on the project.</Description>
      <Listbox name="status" defaultValue="active">
        <ListboxOption value="active">
          <ListboxLabel>Active</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="paused">
          <ListboxLabel>Paused</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="delayed">
          <ListboxLabel>Delayed</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="canceled">
          <ListboxLabel>Canceled</ListboxLabel>
        </ListboxOption>
      </Listbox>
    </Field>
  )
}
With placeholder
Use the placeholder prop to add a placeholder to your Listbox when no value is selected:
import { Field, Label } from '@/components/fieldset'
import { Listbox, ListboxLabel, ListboxOption } from '@/components/listbox'
function Example() {
  return (
    <Field>
      <Label>Project status</Label>
      <Listbox name="status" placeholder="Select status…">
        <ListboxOption value="active">
          <ListboxLabel>Active</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="paused">
          <ListboxLabel>Paused</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="delayed">
          <ListboxLabel>Delayed</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="canceled">
          <ListboxLabel>Canceled</ListboxLabel>
        </ListboxOption>
      </Listbox>
    </Field>
  )
}
import { Field, Label } from '@/components/fieldset'
import { Listbox, ListboxLabel, ListboxOption } from '@/components/listbox'
function Example() {
  return (
    <Field>
      <Label>Project status</Label>
      <Listbox name="status" placeholder="Select status…">
        <ListboxOption value="active">
          <ListboxLabel>Active</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="paused">
          <ListboxLabel>Paused</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="delayed">
          <ListboxLabel>Delayed</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="canceled">
          <ListboxLabel>Canceled</ListboxLabel>
        </ListboxOption>
      </Listbox>
    </Field>
  )
}
With avatars
Add an Avatar to a ListboxOption by inserting it before your ListboxLabel:
import { Avatar } from '@/components/avatar'
import { Field, Label } from '@/components/fieldset'
import { Listbox, ListboxLabel, ListboxOption } from '@/components/listbox'
function Example({ users }) {
  return (
    <Field>
      <Label>Assigned to</Label>
      <Listbox name="user">
        {users.map((user) => (
          <ListboxOption key={user.id} value={user}>
            <Avatar src={user.avatarUrl} initials={user.initial} className="bg-purple-500 text-white" alt="" />
            <ListboxLabel>{user.name}</ListboxLabel>
          </ListboxOption>
        ))}
      </Listbox>
    </Field>
  )
}
import { Avatar } from '@/components/avatar'
import { Field, Label } from '@/components/fieldset'
import { Listbox, ListboxLabel, ListboxOption } from '@/components/listbox'
function Example({ users }) {
  return (
    <Field>
      <Label>Assigned to</Label>
      <Listbox name="user">
        {users.map((user) => (
          <ListboxOption key={user.id} value={user}>
            <Avatar src={user.avatarUrl} initials={user.initial} className="bg-purple-500 text-white" alt="" />
            <ListboxLabel>{user.name}</ListboxLabel>
          </ListboxOption>
        ))}
      </Listbox>
    </Field>
  )
}
With icons
Add an icon to a ListboxOption by inserting it before your ListboxLabel:
import { Field, Label } from '@/components/fieldset'
import { Listbox, ListboxLabel, ListboxOption } from '@/components/listbox'
import { Bars3BottomLeftIcon, Bars3BottomRightIcon, Bars3Icon } from '@heroicons/react/16/solid'
function Example() {
  return (
    <Field>
      <Label>Alignment</Label>
      <Listbox name="alignment" defaultValue="left">
        <ListboxOption value="left">
          <Bars3BottomLeftIcon />
          <ListboxLabel>Left</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="right">
          <Bars3BottomRightIcon />
          <ListboxLabel>Right</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="justified">
          <Bars3Icon />
          <ListboxLabel>Justified</ListboxLabel>
        </ListboxOption>
      </Listbox>
    </Field>
  )
}
import { Field, Label } from '@/components/fieldset'
import { Listbox, ListboxLabel, ListboxOption } from '@/components/listbox'
import { Bars3BottomLeftIcon, Bars3BottomRightIcon, Bars3Icon } from '@heroicons/react/16/solid'
function Example() {
  return (
    <Field>
      <Label>Alignment</Label>
      <Listbox name="alignment" defaultValue="left">
        <ListboxOption value="left">
          <Bars3BottomLeftIcon />
          <ListboxLabel>Left</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="right">
          <Bars3BottomRightIcon />
          <ListboxLabel>Right</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="justified">
          <Bars3Icon />
          <ListboxLabel>Justified</ListboxLabel>
        </ListboxOption>
      </Listbox>
    </Field>
  )
}
The ListboxOption component is designed to work best with 16×16 icons.
With flags
Add a flag icon to a ListboxOption by inserting it before your ListboxLabel, just like any other icon:
import { Field, Label } from '@/components/fieldset'
import { Listbox, ListboxLabel, ListboxOption } from '@/components/listbox'
import { Flag } from '@/flags'
function Example({ currentCountry, countries }) {
  return (
    <Field>
      <Label>Assigned to</Label>
      <Listbox name="user" defaultValue={currentCountry}>
        {countries.map((country) => (
          <ListboxOption value={country.code} key={country.code}>
            <Flag className="w-5 sm:w-4" code={country.code} />
            <ListboxLabel>{country.name}</ListboxLabel>
          </ListboxOption>
        ))}
      </Listbox>
    </Field>
  )
}
import { Field, Label } from '@/components/fieldset'
import { Listbox, ListboxLabel, ListboxOption } from '@/components/listbox'
import { Flag } from '@/flags'
function Example({ currentCountry, countries }) {
  return (
    <Field>
      <Label>Assigned to</Label>
      <Listbox name="user" defaultValue={currentCountry}>
        {countries.map((country) => (
          <ListboxOption value={country.code} key={country.code}>
            <Flag className="w-5 sm:w-4" code={country.code} />
            <ListboxLabel>{country.name}</ListboxLabel>
          </ListboxOption>
        ))}
      </Listbox>
    </Field>
  )
}
We like the 16×12 flag icons from Flagpack, a great set of open-source flag icons.
With secondary text
Use the ListboxDescription component to add secondary text to a listbox option:
import { Field, Label } from '@/components/fieldset'
import { Listbox, ListboxDescription, ListboxLabel, ListboxOption } from '@/components/listbox'
function Example({ users }) {
  return (
    <Field>
      <Label>User</Label>
      <Listbox name="user">
        {users.map((user) => (
          <ListboxOption key={user.id} value={user}>
            <ListboxLabel>{user.name}</ListboxLabel>
            <ListboxDescription>@{user.handle}</ListboxDescription>
          </ListboxOption>
        ))}
      </Listbox>
    </Field>
  )
}
import { Field, Label } from '@/components/fieldset'
import { Listbox, ListboxDescription, ListboxLabel, ListboxOption } from '@/components/listbox'
function Example({ users }) {
  return (
    <Field>
      <Label>User</Label>
      <Listbox name="user">
        {users.map((user) => (
          <ListboxOption key={user.id} value={user}>
            <ListboxLabel>{user.name}</ListboxLabel>
            <ListboxDescription>@{user.handle}</ListboxDescription>
          </ListboxOption>
        ))}
      </Listbox>
    </Field>
  )
}
Disabled state
Add the disabled prop to the Field component to disable a Listbox and the associated Label:
import { Field, Label } from '@/components/fieldset'
import { Listbox, ListboxLabel, ListboxOption } from '@/components/listbox'
function Example() {
  return (
    <Field disabled>
      <Label>Project status</Label>
      <Listbox name="status" defaultValue="delayed">
        <ListboxOption value="active">
          <ListboxLabel>Active</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="paused">
          <ListboxLabel>Paused</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="delayed">
          <ListboxLabel>Delayed</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="canceled">
          <ListboxLabel>Canceled</ListboxLabel>
        </ListboxOption>
      </Listbox>
    </Field>
  )
}
import { Field, Label } from '@/components/fieldset'
import { Listbox, ListboxLabel, ListboxOption } from '@/components/listbox'
function Example() {
  return (
    <Field disabled>
      <Label>Project status</Label>
      <Listbox name="status" defaultValue="delayed">
        <ListboxOption value="active">
          <ListboxLabel>Active</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="paused">
          <ListboxLabel>Paused</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="delayed">
          <ListboxLabel>Delayed</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="canceled">
          <ListboxLabel>Canceled</ListboxLabel>
        </ListboxOption>
      </Listbox>
    </Field>
  )
}
You can also disable a listbox outside of a Field by adding the disabled prop directly to the Listbox itself.
Validation errors
Add the invalid prop to the Listbox component to indicate a validation error, and render the error using the
ErrorMessage component:
A project status is required.
import { ErrorMessage, Field, Label } from '@/components/fieldset'
import { Listbox, ListboxLabel, ListboxOption } from '@/components/listbox'
function Example({ errors }) {
  return (
    <Field invalid>
      <Label>Project status</Label>
      <Listbox name="status" placeholder="Select a status…" invalid={errors.has('status')}>
        <ListboxOption value="active">
          <ListboxLabel>Active</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="paused">
          <ListboxLabel>Paused</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="delayed">
          <ListboxLabel>Delayed</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="canceled">
          <ListboxLabel>Canceled</ListboxLabel>
        </ListboxOption>
      </Listbox>
      {errors.has('status') && <ErrorMessage>{errors.get('status')}</ErrorMessage>}
    </Field>
  )
}
import { ErrorMessage, Field, Label } from '@/components/fieldset'
import { Listbox, ListboxLabel, ListboxOption } from '@/components/listbox'
function Example({ errors }) {
  return (
    <Field invalid>
      <Label>Project status</Label>
      <Listbox name="status" placeholder="Select a status…" invalid={errors.has('status')}>
        <ListboxOption value="active">
          <ListboxLabel>Active</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="paused">
          <ListboxLabel>Paused</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="delayed">
          <ListboxLabel>Delayed</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="canceled">
          <ListboxLabel>Canceled</ListboxLabel>
        </ListboxOption>
      </Listbox>
      {errors.has('status') && <ErrorMessage>{errors.get('status')}</ErrorMessage>}
    </Field>
  )
}
Constraining width
Use the className prop on the Listbox component to make layout adjustments like adjusting the max-width:
import { Field, Label } from '@/components/fieldset'
import { Listbox, ListboxLabel, ListboxOption } from '@/components/listbox'
function Example() {
  return (
    <Field>
      <Label>Day of the week</Label>
      <Listbox className="max-w-40" name="day_of_the_week" defaultValue="Monday">
        <ListboxOption value="Monday">
          <ListboxLabel>Monday</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="Tuesday">
          <ListboxLabel>Tuesday</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="Wednesday">
          <ListboxLabel>Wednesday</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="Thursday">
          <ListboxLabel>Thursday</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="Friday">
          <ListboxLabel>Friday</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="Saturday">
          <ListboxLabel>Saturday</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="Sunday">
          <ListboxLabel>Sunday</ListboxLabel>
        </ListboxOption>
      </Listbox>
    </Field>
  )
}
import { Field, Label } from '@/components/fieldset'
import { Listbox, ListboxLabel, ListboxOption } from '@/components/listbox'
function Example() {
  return (
    <Field>
      <Label>Day of the week</Label>
      <Listbox className="max-w-40" name="day_of_the_week" defaultValue="Monday">
        <ListboxOption value="Monday">
          <ListboxLabel>Monday</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="Tuesday">
          <ListboxLabel>Tuesday</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="Wednesday">
          <ListboxLabel>Wednesday</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="Thursday">
          <ListboxLabel>Thursday</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="Friday">
          <ListboxLabel>Friday</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="Saturday">
          <ListboxLabel>Saturday</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="Sunday">
          <ListboxLabel>Sunday</ListboxLabel>
        </ListboxOption>
      </Listbox>
    </Field>
  )
}
Be aware that the className prop is a sharp knife — make sure to only add classes that don't conflict with classes the
component already includes or you'll get unexpected results.
With custom layout
Use the unstyled Field component from @headlessui/react directly instead of the styled Field component to
implement a custom layout:
import { Label } from '@/components/fieldset'
import { Listbox, ListboxLabel, ListboxOption } from '@/components/listbox'
import * as Headless from '@headlessui/react'
function Example() {
  return (
    <Headless.Field className="flex items-baseline justify-center gap-6">
      <Label>Project status</Label>
      <Listbox name="status" defaultValue="active" className="max-w-48">
        <ListboxOption value="active">
          <ListboxLabel>Active</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="paused">
          <ListboxLabel>Paused</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="delayed">
          <ListboxLabel>Delayed</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="canceled">
          <ListboxLabel>Canceled</ListboxLabel>
        </ListboxOption>
      </Listbox>
    </Headless.Field>
  )
}
import { Label } from '@/components/fieldset'
import { Listbox, ListboxLabel, ListboxOption } from '@/components/listbox'
import * as Headless from '@headlessui/react'
function Example() {
  return (
    <Headless.Field className="flex items-baseline justify-center gap-6">
      <Label>Project status</Label>
      <Listbox name="status" defaultValue="active" className="max-w-48">
        <ListboxOption value="active">
          <ListboxLabel>Active</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="paused">
          <ListboxLabel>Paused</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="delayed">
          <ListboxLabel>Delayed</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="canceled">
          <ListboxLabel>Canceled</ListboxLabel>
        </ListboxOption>
      </Listbox>
    </Headless.Field>
  )
}
Using the unstyled Field component will ensure important accessibility features are still handled for you like
generating IDs and associating elements using aria-* attributes.
Controlled component
Use the normal value and onChange props to use the Listbox component as a controlled component:
import { Field, Label } from '@/components/fieldset'
import { Listbox, ListboxLabel, ListboxOption } from '@/components/listbox'
import { useState } from 'react'
function Example() {
  let [status, setStatus] = useState('active')
  return (
    <Field>
      <Label>Project status</Label>
      <Listbox name="status" value={status} onChange={setStatus}>
        <ListboxOption value="active">
          <ListboxLabel>Active</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="paused">
          <ListboxLabel>Paused</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="delayed">
          <ListboxLabel>Delayed</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="canceled">
          <ListboxLabel>Canceled</ListboxLabel>
        </ListboxOption>
      </Listbox>
    </Field>
  )
}
import { Field, Label } from '@/components/fieldset'
import { Listbox, ListboxLabel, ListboxOption } from '@/components/listbox'
import { useState } from 'react'
function Example() {
  let [status, setStatus] = useState('active')
  return (
    <Field>
      <Label>Project status</Label>
      <Listbox name="status" value={status} onChange={setStatus}>
        <ListboxOption value="active">
          <ListboxLabel>Active</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="paused">
          <ListboxLabel>Paused</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="delayed">
          <ListboxLabel>Delayed</ListboxLabel>
        </ListboxOption>
        <ListboxOption value="canceled">
          <ListboxLabel>Canceled</ListboxLabel>
        </ListboxOption>
      </Listbox>
    </Field>
  )
}