Advanced selection interfaces including comboboxes, listboxes, radio groups with keyboard navigation, filtering, and full accessibility support.
An autocomplete combobox component for searchable selection with keyboard navigation and filtering.
/**
* Autocomplete combobox component for searchable selection
* @param props - Combobox properties including value management and behavior options
*/
function Combobox<TValue = string>(
props: ComboboxProps<TValue>
): JSX.Element;
interface ComboboxProps<TValue = string> extends PolymorphicProps<'div'> {
/** Current value (controlled) */
value?: TValue;
/** Default value (uncontrolled) */
defaultValue?: TValue;
/** Value change handler */
onChange?: (value: TValue) => void;
/** Function to compare values for equality */
by?: ByComparator<TValue>;
/** Whether multiple selection is allowed */
multiple?: boolean;
/** Whether combobox is disabled */
disabled?: boolean;
/** Whether combobox is in invalid state */
invalid?: boolean;
/** Associated form ID */
form?: string;
/** Form field name */
name?: string;
/** Whether to open on focus immediately */
immediate?: boolean;
/** Virtual scrolling configuration for large lists */
virtual?: {
options: TValue[];
disabled?: (value: TValue) => boolean;
};
/** Close handler */
onClose?: () => void;
/** Render prop providing combobox state */
children?: React.ReactNode | ((props: ComboboxRenderProps<TValue>) => React.ReactNode);
}
interface ComboboxRenderProps<TValue = string> {
/** Whether combobox is open */
open: boolean;
/** Whether disabled */
disabled: boolean;
/** Whether invalid */
invalid: boolean;
/** Index of active option */
activeIndex: number | null;
/** Active option value */
activeOption: TValue | null;
/** Current value */
value: TValue;
}The input element for the combobox with display value formatting.
/**
* Input element for the combobox
* @param props - ComboboxInput properties
*/
function ComboboxInput<TType = string>(
props: ComboboxInputProps<TType>
): JSX.Element;
interface ComboboxInputProps<TType = string> extends PolymorphicProps<'input'> {
/** Default input value */
defaultValue?: TType;
/** Whether input is disabled */
disabled?: boolean;
/** Function to format displayed value */
displayValue?: (item: TType) => string;
/** Change handler for input value */
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
/** Whether to auto-focus on mount */
autoFocus?: boolean;
/** Render prop providing input state */
children?: React.ReactNode | ((props: ComboboxInputRenderProps) => React.ReactNode);
}
interface ComboboxInputRenderProps {
/** Whether combobox is open */
open: boolean;
/** Whether disabled */
disabled: boolean;
/** Whether invalid */
invalid: boolean;
/** Whether being hovered */
hover: boolean;
/** Whether has focus */
focus: boolean;
/** Whether has autofocus */
autofocus: boolean;
}Button to toggle the combobox dropdown.
/**
* Button to toggle the combobox
* @param props - ComboboxButton properties
*/
function ComboboxButton<TTag extends keyof JSX.IntrinsicElements = 'button'>(
props: ComboboxButtonProps<TTag>
): JSX.Element;
interface ComboboxButtonProps<TTag extends keyof JSX.IntrinsicElements = 'button'>
extends PolymorphicProps<TTag> {
/** Whether to auto-focus on mount */
autoFocus?: boolean;
/** Whether button is disabled */
disabled?: boolean;
/** Render prop providing button state */
children?: React.ReactNode | ((props: ComboboxButtonRenderProps) => React.ReactNode);
}
interface ComboboxButtonRenderProps {
/** Whether combobox is open */
open: boolean;
/** Whether button is active/pressed */
active: boolean;
/** Whether disabled */
disabled: boolean;
/** Whether invalid */
invalid: boolean;
/** Current combobox value */
value: any;
/** Whether has focus */
focus: boolean;
/** Whether being hovered */
hover: boolean;
}Container for combobox options with floating UI positioning.
/**
* Container for combobox options
* @param props - ComboboxOptions properties
*/
function ComboboxOptions<TTag extends keyof JSX.IntrinsicElements = 'div'>(
props: ComboboxOptionsProps<TTag>
): JSX.Element;
interface ComboboxOptionsProps<TTag extends keyof JSX.IntrinsicElements = 'div'>
extends PolymorphicProps<TTag> {
/** Whether to hold focus on options */
hold?: boolean;
/** Floating UI anchor configuration */
anchor?: AnchorProps;
/** Whether to render in portal */
portal?: boolean;
/** Whether to use modal behavior */
modal?: boolean;
/** Whether to use transition */
transition?: boolean;
/** Render prop providing options state */
children?: React.ReactNode | ((props: ComboboxOptionsRenderProps) => React.ReactNode);
}
interface ComboboxOptionsRenderProps {
/** Whether combobox is open */
open: boolean;
/** Current option (in render function) */
option: any;
}Individual option within the combobox.
/**
* Individual option within the combobox
* @param props - ComboboxOption properties
*/
function ComboboxOption<TType = string>(
props: ComboboxOptionProps<TType>
): JSX.Element;
interface ComboboxOptionProps<TType = string> extends PolymorphicProps<'div'> {
/** Whether option is disabled */
disabled?: boolean;
/** Option value */
value: TType;
/** Display order */
order?: number;
/** Render prop providing option state */
children?: React.ReactNode | ((props: ComboboxOptionRenderProps) => React.ReactNode);
}
interface ComboboxOptionRenderProps {
/** Whether option has focus */
focus: boolean;
/** Whether option is active (deprecated, use focus) */
active: boolean;
/** Whether option is selected */
selected: boolean;
/** Whether option is disabled */
disabled: boolean;
}Usage Examples:
import { useState } from "react";
import {
Combobox,
ComboboxInput,
ComboboxButton,
ComboboxOptions,
ComboboxOption
} from "@headlessui/react";
const people = [
{ id: 1, name: 'Wade Cooper' },
{ id: 2, name: 'Arlene Mccoy' },
{ id: 3, name: 'Devon Webb' },
];
function ComboboxExample() {
const [selectedPerson, setSelectedPerson] = useState(people[0]);
const [query, setQuery] = useState('');
const filteredPeople = query === ''
? people
: people.filter((person) =>
person.name.toLowerCase().includes(query.toLowerCase())
);
return (
<Combobox value={selectedPerson} onChange={setSelectedPerson}>
<div className="relative">
<ComboboxInput
className="w-full border border-gray-300 rounded py-2 px-3"
displayValue={(person) => person?.name}
onChange={(event) => setQuery(event.target.value)}
/>
<ComboboxButton className="absolute right-2 top-2">
<ChevronDownIcon className="w-5 h-5" />
</ComboboxButton>
</div>
<ComboboxOptions className="absolute z-10 mt-1 w-full bg-white border border-gray-300 rounded shadow-lg">
{filteredPeople.map((person) => (
<ComboboxOption key={person.id} value={person}>
{({ focus, selected }) => (
<div className={`px-3 py-2 cursor-pointer ${
focus ? 'bg-blue-500 text-white' : 'text-gray-900'
}`}>
{person.name}
{selected && <CheckIcon className="w-5 h-5 ml-2" />}
</div>
)}
</ComboboxOption>
))}
</ComboboxOptions>
</Combobox>
);
}
// Multi-select combobox
function MultiSelectExample() {
const [selectedPeople, setSelectedPeople] = useState([]);
return (
<Combobox value={selectedPeople} onChange={setSelectedPeople} multiple>
{/* ... similar structure with multiple selection support ... */}
</Combobox>
);
}A listbox selection component with single or multiple selection capabilities.
/**
* Listbox selection component
* @param props - Listbox properties including value management and orientation
*/
function Listbox<TType = string>(
props: ListboxProps<TType>
): JSX.Element;
interface ListboxProps<TType = string> extends PolymorphicProps<'div'> {
/** Current value */
value?: TType;
/** Default value */
defaultValue?: TType;
/** Value change handler */
onChange?: (value: TType) => void;
/** Function to compare values for equality */
by?: ByComparator<TType>;
/** Whether listbox is disabled */
disabled?: boolean;
/** Whether listbox is invalid */
invalid?: boolean;
/** Whether horizontal orientation */
horizontal?: boolean;
/** Associated form ID */
form?: string;
/** Form field name */
name?: string;
/** Whether multiple selection is allowed */
multiple?: boolean;
/** Render prop providing listbox state */
children?: React.ReactNode | ((props: ListboxRenderProps<TType>) => React.ReactNode);
}
interface ListboxRenderProps<TType = string> {
/** Whether listbox is open */
open: boolean;
/** Whether disabled */
disabled: boolean;
/** Whether invalid */
invalid: boolean;
/** Current value */
value: TType;
}Button to toggle the listbox dropdown.
/**
* Button to toggle the listbox
* @param props - ListboxButton properties
*/
function ListboxButton<TTag extends keyof JSX.IntrinsicElements = 'button'>(
props: ListboxButtonProps<TTag>
): JSX.Element;
interface ListboxButtonProps<TTag extends keyof JSX.IntrinsicElements = 'button'>
extends PolymorphicProps<TTag> {
/** Whether button is disabled */
disabled?: boolean;
/** Whether to auto-focus on mount */
autoFocus?: boolean;
/** Render prop providing button state */
children?: React.ReactNode | ((props: ListboxButtonRenderProps) => React.ReactNode);
}
interface ListboxButtonRenderProps {
/** Whether listbox is open */
open: boolean;
/** Whether button is active */
active: boolean;
/** Whether being hovered */
hover: boolean;
/** Whether has focus */
focus: boolean;
/** Whether disabled */
disabled: boolean;
/** Whether has autofocus */
autofocus: boolean;
}Container for listbox options.
/**
* Container for listbox options
* @param props - ListboxOptions properties
*/
function ListboxOptions<TTag extends keyof JSX.IntrinsicElements = 'div'>(
props: ListboxOptionsProps<TTag>
): JSX.Element;
interface ListboxOptionsProps<TTag extends keyof JSX.IntrinsicElements = 'div'>
extends PolymorphicProps<TTag> {
/** Whether to hold focus on options */
hold?: boolean;
/** Floating UI anchor configuration */
anchor?: AnchorProps;
/** Whether to render in portal */
portal?: boolean;
/** Whether to use modal behavior */
modal?: boolean;
/** Whether to use transition */
transition?: boolean;
/** Render prop providing options state */
children?: React.ReactNode | ((props: ListboxOptionsRenderProps) => React.ReactNode);
}
interface ListboxOptionsRenderProps {
/** Whether listbox is open */
open: boolean;
}Individual option within the listbox.
/**
* Individual option within the listbox
* @param props - ListboxOption properties
*/
function ListboxOption<TType = string>(
props: ListboxOptionProps<TType>
): JSX.Element;
interface ListboxOptionProps<TType = string> extends PolymorphicProps<'div'> {
/** Whether option is disabled */
disabled?: boolean;
/** Option value */
value: TType;
/** Display order */
order?: number;
/** Render prop providing option state */
children?: React.ReactNode | ((props: ListboxOptionRenderProps) => React.ReactNode);
}
interface ListboxOptionRenderProps {
/** Whether option has focus */
focus: boolean;
/** Whether option is active (deprecated, use focus) */
active: boolean;
/** Whether option is selected */
selected: boolean;
/** Whether option is disabled */
disabled: boolean;
}Usage Examples:
import { useState } from "react";
import {
Listbox,
ListboxButton,
ListboxOptions,
ListboxOption
} from "@headlessui/react";
const options = ['Option 1', 'Option 2', 'Option 3'];
function ListboxExample() {
const [selected, setSelected] = useState(options[0]);
return (
<Listbox value={selected} onChange={setSelected}>
<div className="relative">
<ListboxButton className="w-full border border-gray-300 rounded py-2 px-3 text-left">
{selected}
<ChevronDownIcon className="w-5 h-5 float-right" />
</ListboxButton>
<ListboxOptions className="absolute z-10 mt-1 w-full bg-white border border-gray-300 rounded shadow-lg">
{options.map((option) => (
<ListboxOption key={option} value={option}>
{({ focus, selected }) => (
<div className={`px-3 py-2 cursor-pointer ${
focus ? 'bg-blue-500 text-white' : 'text-gray-900'
}`}>
{option}
{selected && <CheckIcon className="w-5 h-5 ml-2" />}
</div>
)}
</ListboxOption>
))}
</ListboxOptions>
</div>
</Listbox>
);
}A radio group component for single selection from multiple options.
/**
* Radio group for single selection
* @param props - RadioGroup properties
*/
function RadioGroup<TValue = string>(
props: RadioGroupProps<TValue>
): JSX.Element;
interface RadioGroupProps<TValue = string> extends PolymorphicProps<'div'> {
/** Current value */
value?: TValue;
/** Default value */
defaultValue?: TValue;
/** Value change handler */
onChange?: (value: TValue) => void;
/** Function to compare values for equality */
by?: ByComparator<TValue>;
/** Whether radio group is disabled */
disabled?: boolean;
/** Associated form ID */
form?: string;
/** Form field name */
name?: string;
/** Render prop providing radio group state */
children?: React.ReactNode | ((props: RadioGroupRenderProps<TValue>) => React.ReactNode);
}
interface RadioGroupRenderProps<TValue = string> {
/** Current value */
value: TValue;
/** Whether disabled */
disabled: boolean;
}Individual radio option within the group.
/**
* Individual radio option
* @param props - RadioGroupOption properties
*/
function RadioGroupOption<TValue = string>(
props: RadioGroupOptionProps<TValue>
): JSX.Element;
interface RadioGroupOptionProps<TValue = string> extends PolymorphicProps<'div'> {
/** Option value */
value: TValue;
/** Whether option is disabled */
disabled?: boolean;
/** Display order */
order?: number;
/** Render prop providing option state */
children?: React.ReactNode | ((props: RadioGroupOptionRenderProps) => React.ReactNode);
}
interface RadioGroupOptionRenderProps {
/** Whether option is checked */
checked: boolean;
/** Whether option is active/pressed */
active: boolean;
/** Whether being hovered */
hover: boolean;
/** Whether has focus */
focus: boolean;
/** Whether disabled */
disabled: boolean;
}Individual radio input component (can be used independently).
/**
* Individual radio input component
* @param props - Radio properties
*/
function Radio<TValue = string>(
props: RadioProps<TValue>
): JSX.Element;
interface RadioProps<TValue = string> extends PolymorphicProps<'input'> {
/** Radio value */
value: TValue;
/** Whether radio is disabled */
disabled?: boolean;
/** Whether to auto-focus on mount */
autoFocus?: boolean;
/** Associated form ID */
form?: string;
/** Form field name */
name?: string;
/** Change handler */
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
/** Tab index */
tabIndex?: number;
/** Render prop providing radio state */
children?: React.ReactNode | ((props: RadioRenderProps) => React.ReactNode);
}
interface RadioRenderProps {
/** Whether radio is checked */
checked: boolean;
/** Whether currently changing state */
changing: boolean;
/** Whether has focus */
focus: boolean;
/** Whether being pressed */
active: boolean;
/** Whether being hovered */
hover: boolean;
/** Whether has autofocus */
autofocus: boolean;
/** Whether disabled */
disabled: boolean;
}Usage Examples:
import { useState } from "react";
import { RadioGroup, RadioGroupOption, Radio } from "@headlessui/react";
const plans = [
{ name: 'Startup', price: '$29/month' },
{ name: 'Business', price: '$99/month' },
{ name: 'Enterprise', price: '$249/month' },
];
function RadioGroupExample() {
const [selected, setSelected] = useState(plans[0]);
return (
<RadioGroup value={selected} onChange={setSelected}>
<div className="space-y-2">
{plans.map((plan) => (
<RadioGroupOption key={plan.name} value={plan}>
{({ checked }) => (
<div className={`${checked ? 'bg-blue-500 text-white' : 'bg-white text-gray-900'}
relative rounded-lg px-5 py-4 cursor-pointer border border-gray-300
focus:outline-none focus:ring-2 focus:ring-blue-500`}>
<div className="flex items-center justify-between">
<div>
<p className="font-medium">{plan.name}</p>
<p className="text-sm">{plan.price}</p>
</div>
{checked && <CheckIcon className="w-6 h-6" />}
</div>
</div>
)}
</RadioGroupOption>
))}
</div>
</RadioGroup>
);
}
// Standalone radio buttons
function StandaloneRadioExample() {
return (
<form>
<div className="space-y-2">
<label className="flex items-center">
<Radio name="plan" value="startup" />
<span className="ml-2">Startup Plan</span>
</label>
<label className="flex items-center">
<Radio name="plan" value="business" />
<span className="ml-2">Business Plan</span>
</label>
</div>
</form>
);
}// Value comparison function type
type ByComparator<T> =
| ((a: T, z: T) => boolean)
| keyof T
| null;
// Floating UI anchor configuration
interface AnchorProps {
to?: string;
gap?: number;
offset?: number;
padding?: number;
}
// Common option render props
interface BaseOptionRenderProps {
focus: boolean;
active: boolean; // Deprecated in favor of focus
selected: boolean;
disabled: boolean;
}
// Common selection component render props
interface BaseSelectionRenderProps<TValue = any> {
open: boolean;
disabled: boolean;
invalid?: boolean;
value: TValue;
}