@headlessui/react@v2.0.0
We just released Headless UI v2.0 for React which includes a ton of new stuff:
- Built-in anchor positioning — using Floating UI, components like
Menu,Listbox, and more can now automatically position their popovers to be anchored to their trigger, adapting as needed to changes in the viewport. - Headless checkbox component — we've added a headless
Checkboxcomponent to complement our existingRadioGroupcomponent, making it easy to build totally custom checkbox controls. - HTML form components — we've added
Input,Select,Textarea,Label,Description,Fieldset, andLegendcomponents that handle all of the ID generation andaria-*attribute mapping you need to do to connect form fields together. - Improved hover and focus-visible detection — using hooks from the awesome React Aria library under the hood, Headless UI now adds smarter
data-hoveranddata-focusattributes to your controls that behave more consistently across different devices than the native pseudo-classes. - Combobox list virtualization — the
Comboboxcomponent can now handle giant lists of options with no performance issues.
Changelog
Added
- Add new
Checkboxcomponent (#2887, #2962) - Add new
Buttoncomponent (#2887) - Add new
Inputcomponent (#2887, #2902, #2940) - Add new
Textareacomponent (#2887, #2902, #2940) - Add new
Selectcomponent (#2887, #2902) - Add new
FieldsetandLegendcomponents (#2887) - Add new
Field,Label,Descriptioncomponents (#2887, #2941) - Add new
MenuSection,MenuHeading, andMenuSeparatorcomponents (#2887) - Add new
ListboxSelectedOptioncomponent (#2887) - Add new
DataInteractivecomponent (#2887) - Add new
CloseButtoncomponent anduseClosehook (#3096) - Add new
anchor,modal, andportalprops toCombobox,Listbox,MenuandPopovercomponents (#2887, #3075, #3097, #3111, #3115, #3121, #3124, #3133, #3138, #3148, #3152, #3154, #3157) - Add new
autoFocusprop to focusable components (#2887) - Add new
virtualprop toComboboxcomponent (#2779, #3128, #3160, #3161, #3163) - Add new
onCloseprop toComboboxcomponent (#3122) - Add new
immediateprop toComboboxfor immediately opening the Combobox when theinputreceives focus (#2686) - Add new
--button-widthCSS variable on theListboxOptions,MenuItems, andPopoverPanelcomponents (#2887, #3058) - Add new
--input-widthand--button-widthCSS variables on theComboboxOptionscomponent (#3057) - Add new
data-*attributes as an alternative to the existingdata-headlessui-stateattribute (#2887, #3035, #3061)
Fixed
- Fix scroll-locking on iOS (#2891)
- Fix cancellation of events when using
disabledoraria-disabledattributes (#2890) - Fix unnecessary execution of the
displayValuecallback inComboboxInputcomponent (#3048) - Fix types for
multipleprop inComboboxcomponent (#3099) - Fix focus handling in
ComboboxInputcomponent (#3065, #3073) - Fix enter transitions in
Transitioncomponent (#3074) - Fix focus handling in
ListboxOptionsandMenuItemscomponents (#3112) - Fix horizontal scrolling inside the
Dialogcomponent (#2889)
Changed
- Require React 18 (#2887, #3092, #3131)
- Always render hidden form input fields for
Checkbox,SwitchandRadioGroupcomponents (#3095) - Deprecate the
RadioGroup.Optioncomponent in favor of newRadiocomponent (#2887) - Deprecate the
activeprop in favor of newfocusprop (#2887) - Dialog is now focused by default instead of the first focusable element (#2887)
- Change default tags for
ListboxOptions,ListboxOption,ComboboxOptions,ComboboxOption, andTabGroupcomponents (#3109) - Change default tag from
divtoFragmentonTransitioncomponents (#3110, #3147) - Allow
Comboboxcomponent to havenullvalue (#3064, #3100) - Attempt form submission when pressing enter on the
ListboxButtoncomponent (#2972) - Deprecate the
enteredprop on theTransitioncomponent (#3089) - Add frozen value to
ComboboxOptionscomponent (#3126)
Upgrading from v1
Update dependencies
Install the latest version of the package via npm:
npm install @headlessui/react@latestDefault element changes
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" ...>Comboboxes now always support empty values
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('')}
>Dialogs no longer require a focusable child
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>Dropdown components are now modal by default
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 removed
In 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>Deprecated component dot-notation
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>
)
}Deprecated component-specific label and description components
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>
)
}Deprecated RadioGroup.Option component
We'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.
Deprecated active prop in favor of focus
We'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>Deprecated @headlessui/tailwindcss package
With 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/tailwindcssNext, 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>