ListboxOptions (#3337)unmount on Dialog works in combination with the transition prop on DialogBackdrop and DialogPanel components (#3352)Combobox component when in virtual mode when options are empty (#3356)anchor prop (#3357)transition and focus prop combination for PopoverPanel component (#3361)Popover components (#3362)Dialog component (#3365)flushSync warning for Combobox component with immediate prop enabled (#3366)We just released Headless UI v2.1 for React, which dramatically simplifies our transition APIs and adds support for rendering multiple dialogs as siblings. See our announcement blog to learn more.
Dialog components at once (without nesting them) (#3242)DialogBackdrop component (#3307, #3310)PopoverBackdrop component to replace PopoverOverlay (#3308)Combobox open when clicking scrollbar in ComboboxOptions (#3249)ComboboxInput does not sync with current value while typing (#3259)Combobox component when using native scrollbar (#3190)ComboboxOptions while closing (#3304)style prop on ComboboxOptions, ListboxOptions, MenuItems, and PopoverPanel components (#3250)Checkbox when it is disabled (#3251)useId instead of React internals (for React 19 compatibility) (#3254)Transition and TransitionChild components (#3303)Tab and Shift+Tab keys when the Listbox component is open (#3284)Escape to close the Dialog component (#3218)virtual and non-virtual mode in Combobox component (#3236)PopoverPanel component moves focus inside (without using PortalGroup) (#3239)defaultValue is used (#3240)SwitchGroup as deprecated, prefer Field instead (#3232)fieldset instead of div by default for Fieldset component (#3237)Combobox works (#3182)useInertOthers hook (#3181)DialogPanel and DialogTitle (#3176)We just released Headless UI v2.0 for React which includes a ton of new stuff:
Menu, Listbox, and more can now automatically position their popovers to be anchored to their trigger, adapting as needed to changes in the viewport.Checkbox component to complement our existing RadioGroup component, making it easy to build totally custom checkbox controls.Input, Select, Textarea, Label, Description, Fieldset, and Legend components that handle all of the ID generation and aria-* attribute mapping you need to do to connect form fields together.data-hover and data-focus attributes to your controls that behave more consistently across different devices than the native pseudo-classes.Combobox component can now handle giant lists of options with no performance issues.Checkbox component (#2887, #2962)Button component (#2887)Input component (#2887, #2902, #2940)Textarea component (#2887, #2902, #2940)Select component (#2887, #2902)Fieldset and Legend components (#2887)Field, Label, Description components (#2887, #2941)MenuSection, MenuHeading, and MenuSeparator components (#2887)ListboxSelectedOption component (#2887)DataInteractive component (#2887)CloseButton component and useClose hook (#3096)anchor, modal, and portal props to Combobox, Listbox, Menu and Popover components (#2887, #3075, #3097, #3111, #3115, #3121, #3124, #3133, #3138, #3148, #3152, #3154, #3157)autoFocus prop to focusable components (#2887)virtual prop to Combobox component (#2779, #3128, #3160, #3161, #3163)onClose prop to Combobox component (#3122)immediate prop to Combobox for immediately opening the Combobox when the input receives focus (#2686)--button-width CSS variable on the ListboxOptions, MenuItems, and PopoverPanel components (#2887, #3058)--input-width and --button-width CSS variables on the ComboboxOptions component (#3057)data-* attributes as an alternative to the existing data-headlessui-state attribute (#2887, #3035, #3061)disabled or aria-disabled attributes (#2890)displayValue callback in ComboboxInput component (#3048)multiple prop in Combobox component (#3099)ComboboxInput component (#3065, #3073)Transition component (#3074)ListboxOptions and MenuItems components (#3112)Dialog component (#2889)Checkbox, Switch and RadioGroup components (#3095)RadioGroup.Option component in favor of new Radio component (#2887)active prop in favor of new focus prop (#2887)ListboxOptions, ListboxOption, ComboboxOptions, ComboboxOption, and TabGroup components (#3109)div to Fragment on Transition components (#3110, #3147)Combobox component to have null value (#3064, #3100)ListboxButton component (#2972)entered prop on the Transition component (#3089)ComboboxOptions component (#3126)Install the latest version of the package via npm:
npm install @headlessui/react@latest
The default rendered element has changed for a number of components in v2.0:
| Component | v1.x element | v2.0 element |
|---|---|---|
Transition | div | Fragment |
TransitionChild | div | Fragment |
ListboxOptions | ul | div |
ListboxOption | li | div |
ComboboxOptions | ul | div |
ComboboxOption | li | div |
TabGroup | Fragment | div |
If you are using any of these components without the as prop, you'll need to add the as prop and render the component with the element you need:
- <Transition ...>
+ <Transition as="div" ...>
Previously the Combobox component required an option to always be set, and you could opt out of this behavior using the nullable prop. In v2.0 comboboxes support empty values by default so you can remove this prop:
- <Combobox value={selected} onChange={setSelected} nullable>
+ <Combobox value={selected} onChange={setSelected}>
If you'd like to keep the previous behavior, update your onChange callback to only set the selected value if an option has actually been selected:
<Combobox
value={selected}
- onChange={setSelected}
+ onChange={(newValue) => setSelected((oldValue) => newValue ?? oldValue)}
onClose={() => setQuery('')}
>
Previously the Dialog component would automatically focus the first focusable child on open. This meant that you had to have at least one focusable element within your dialog, otherwise you would see a warning in your console.
In v2.0 that is no longer the case and the dialog's root element is focused instead on open.
If you'd like a child element to receive focus when your dialog is opened, you can add the autoFocus prop to any Headless UI form control:
<Dialog>
<DialogPanel>
- <Listbox ... />
+ <Listbox autoFocus ... />
</DialogPanel>
</Dialog>
For non-Headless UI components, you can add the data-autofocus attribute to any focusable element:
<Dialog>
<DialogPanel>
- <button>...</button>
+ <button data-autofocus>...</button>
</DialogPanel>
</Dialog>
The Menu, Combobox, and Listbox dropdowns are now rendered modal by default. When the dropdown is open the page is scroll-locked and all other page content is made inert.
While this is generally recommended behavior for these components, you can disable this using the modal prop:
- <ComboboxOptions ...>
+ <ComboboxOptions modal={false} ...>
Dialog.Overlay and Dialog.Backdrop components removedIn Headless UI v1.6 we deprecated the Dialog.Overlay and Dialog.Backdrop components. These have now been removed in v2.0.
If you're using either of these components, you can update your app to use the new DialogPanel approach:
<Dialog>
- <Dialog.Overlay className="..." />
+ <div className="..." />
+ <DialogPanel>
{/* ... */}
+ </DialogPanel>
</Dialog>
The previous component dot-notation has been deprecated in favor of using explicit imports for each individual component. This is to improve compatibility with RSC (React Server Components) and tree-shaking.
We recommend updating to this new API:
- import { Menu } from '@headlessui/react'
+ import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/react'
function Example() {
return (
<Menu>
- <Menu.Button>My account</Menu.Button>
- <Menu.Items>
- <Menu.Item><a href="/settings">Settings</a></Menu.Item>
- <Menu.Item><a href="/support">Support</a></Menu.Item>
- <Menu.Item><a href="/license">License</a></Menu.Item>
- </Menu.Items>
+ <MenuButton>My account</MenuButton>
+ <MenuItems>
+ <MenuItem><a href="/settings">Settings</a></MenuItem>
+ <MenuItem><a href="/support">Support</a></MenuItem>
+ <MenuItem><a href="/license">License</a></MenuItem>
+ </MenuItems>
</Menu>
)
}
With the addition of the multi-purpose Field, Label and Description components in v2, we've deprecated the component-specific versions.
| v1.x component | v2.0 replacement |
|---|---|
Combobox.Label | Label |
Dialog.Description | Description |
Listbox.Label | Label |
RadioGroup.Description | Description |
RadioGroup.Label | Label |
Switch.Description | Description |
Switch.Group | Field |
Switch.Label | Label |
We recommend updating to these new components:
- import { Combobox } from '@headlessui/react'
+ import { Combobox, ComboboxInput, ComboboxOptions, Field, Label } from '@headlessui/react'
function Example() {
return (
- <Combobox>
- <Combobox.Label>Assignee:</Combobox.Label>
- <Combobox.Input />
- <Combobox.Options>{/* ... */}</Combobox.Options>
- </Combobox>
+ <Field>
+ <Label>Assignee:</Label>
+ <Combobox>
+ <ComboboxInput />
+ <ComboboxOptions>{/* ... */}</ComboboxOptions>
+ </Combobox>
+ <Field>
)
}
RadioGroup.Option componentWe've deprecated the RadioGroup.Option in favor of the more terse Radio component.
We recommend updating to this new component:
<RadioGroup>
- <RadioGroup.Option>{*/ ...*/}</RadioGroup.Option>
+ <Radio>{*/ ...*/}</Radio>
</RadioGroup>
We've also changed the default tag from div to span.
active prop in favor of focusWe've deprecated the active prop in favor of the new focus prop on the ComboboxOption, ListboxOption, ListboxOption, MenuItem, MenuItem, RadioOption, and RadioOption components.
We recommend updating to this new prop:
<MenuItem >
- {({ active }) => (
+ {({ focus }) => (
{/* ... */}
)}
</MenuItem>
@headlessui/tailwindcss packageWith the availability of the new data-* attributes in v2.0, we've deprecated the @headlessui/tailwindcss package.
We recommend updating to use the new data-* attributes instead. Start by removing this package:
npm remove @headlessui/tailwindcss
Next, replace the ui-* class modifiers with data-[*] modifiers:
<MenuItem
as="a"
href="#"
- className="ui-active:bg-blue-500 ui-active:text-white"
+ className="data-[active]:bg-blue-500 data-[active]:text-white"
>
{*/ ...*/}
</MenuItem>
disabled state on Tab component (#2918)Dialog.Panel (#2919)hidden attribute to internal Hidden component when the Features.Hidden feature is used (#2955)tabIndex on the Switch component (#2966)disabled state to hidden inputs in form-like components (#3004)selectedIndex for controlled Tab components (#3037)disabled state on Tab component (#2918)DialogPanel (#2919)disabled prop for components inside MenuItem (#2929)hidden attribute to internal Hidden component when the Features.Hidden feature is used (#2955)tabIndex on the Switch component (#2966)disabled state to hidden inputs in form-like components (#3004)selectedIndex for controlled Tab components (#3037)activeOption render prop (#2973)<Dialog>'s onClose twice on mobile devices (#2690)TabPanel components are hidden from the accessibility tree (#2708)role="alertdialog" to <Dialog> component (#2709)ComboboxInput component closes the Combobox (#2712)<button> to be in nested components in <PopoverButton> (#2715)data-headlessui-state attribute when as="template" (#2787)Listbox component in Chrome (#2824)<Dialog>'s onClose twice on mobile devices (#2690)<Dialog> (#2697)Tab.Panel components are hidden from the accessibility tree (#2708)role="alertdialog" to <Dialog> component (#2709)Combobox.Input component closes the Combobox (#2712)className prop when the <Transition /> component is currently not transitioning (#2722)<Disclosure.Panel> and <Popover.Panel> when as={Fragment} (#2760)Listbox component in Chrome (#2824)Input, Select and Textarea expose Ref related types (#2902)