Join the Headless UI Community
mainFor help, discussions about best practices, or to share feature ideas, use the GitHub Discussions:
Sources: README.md
repository·main·Indexed Apr 15, 2026
https://github.com/tailwindlabs/headlessuiHeadless UI is a collection of completely unstyled, fully accessible UI components for React and Vue that integrate seamlessly with Tailwind CSS. It handles complex state management, focus trapping, and ARIA attributes, leaving all visual styling to the developer. The repository includes @headlessui/react, @headlessui/vue, and @headlessui/tailwindcss. Features include new form components (Checkbox, Input, Select), data-attribute-based transitions, React 19 support, and performance improvements for Menu, Listbox, and Combobox.
For help, discussions about best practices, or to share feature ideas, use the GitHub Discussions:
Sources: README.md
For help, discussion about best practices, or feature ideas, use the official GitHub Discussions:
This is the recommended place to ask questions or propose new features for the library.
Sources: packages/@headlessui-vue/README.md
<script setup>
import { Switch, SwitchLabel, SwitchDescription } from '@headlessui/vue'
const enabled = true
</script>
<template>
<Switch v-model="enabled">
<SwitchLabel>Enable Dark Mode</SwitchLabel>
<SwitchDescription>
Switch between light and dark themes.
</SwitchDescription>
</Switch>
</template>Headless UI now provides simplified data-* attributes as an alternative to the existing data-headlessui-state="..." attributes. These new attributes make it easier to style components based on their state using Tailwind CSS.
New Attributes:
data-autofocus: Maps to the autoFocus prop on focusable components.data-* attributes for state-based styling (e.g., data-open, data-closed, data-checked, data-unchecked).Usage: You can now use these attributes directly in your Tailwind classes:
<div data-open="" data-closed="">
<span data-open="" class="text-green-500">Open</span>
<span data-closed="" class="text-red-500">Closed</span>
</div><div data-open="" data-closed="">
<span data-open="" class="text-green-500">Open</span>
<span data-closed="" class="text-red-500">Closed</span>
</div>Sources: packages/@headlessui-react/CHANGELOG.md
The library now includes dedicated form components that simplify building accessible forms. These components handle their own state and integrate with the Field system for labels and descriptions.
Available Components:
CheckboxRadio (alternative to RadioGroup.Option)ButtonInputTextareaSelectField, Label, Description, Fieldset, LegendDataInteractiveUsage Pattern:
Wrap your form elements in Field components to automatically handle Label and Description association. Use the form prop on components like RadioGroup, Switch, Listbox, and Combobox to associate them with a specific form.
import { Field, Label, Input, Checkbox } from '@headlessui/react'
function MyForm() {
return (
<form>
<Field>
<Label>Email</Label>
<Input type="email" />
</Field>
<Field>
<Label>
<Checkbox />
Subscribe to newsletter
</Label>
</Field>
</form>
)
}import { Field, Label, Input, Checkbox } from '@headlessui/react'
function MyForm() {
return (
<form>
<Field>
<Label>Email</Label>
<Input type="email" />
</Field>
<Field>
<Label>
<Checkbox />
Subscribe to newsletter
</Label>
</Field>
</form>
)
}Sources: packages/@headlessui-react/CHANGELOG.md
Install the package using npm to add unstyled, fully accessible UI components for React to your project.
npm install @headlessui/reactAfter installation, you can import components from the package in your React files.
npm install @headlessui/reactSources: packages/@headlessui-react/README.md
The Menu component now includes dedicated sub-components for better organization of menu items:
MenuSection: Groups menu items into sections.MenuHeading: Adds a heading within a menu section.MenuSeparator: Adds a visual separator between menu items.Usage:
import { Menu, MenuButton, MenuItem, MenuSection, MenuHeading, MenuSeparator } from '@headlessui/react'
function MyMenu() {
return (
<Menu>
<MenuButton>Menu</MenuButton>
<MenuSection>
<MenuHeading>Settings</MenuHeading>
<MenuItem>Profile</MenuItem>
<MenuItem>Account</MenuItem>
<MenuSeparator />
<MenuItem>Logout</MenuItem>
</MenuSection>
</Menu>
)
}import { Menu, MenuButton, MenuItem, MenuSection, MenuHeading, MenuSeparator } from '@headlessui/react'
function MyMenu() {
return (
<Menu>
<MenuButton>Menu</MenuButton>
<MenuSection>
<MenuHeading>Settings</MenuHeading>
<MenuItem>Profile</MenuItem>
<MenuItem>Account</MenuItem>
<MenuSeparator />
<MenuItem>Logout</MenuItem>
</MenuSection>
</Menu>
)
}Sources: packages/@headlessui-react/CHANGELOG.md
The @headlessui/tailwindcss package provides state-based variants for styling Headless UI components dynamically using Tailwind CSS. To use it, install the package and register the plugin in your tailwind.config.js file.
Installation
npm install @headlessui/tailwindcssConfiguration
Add the plugin to your tailwind.config.js:
// tailwind.config.js
import headlessuiPlugin from '@headlessui/tailwindcss'
export default {
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx,vue}'],
theme: {
extend: {},
},
plugins: [
headlessuiPlugin(),
],
}Custom Prefix
By default, the plugin uses the ui prefix (e.g., ui-open, ui-checked). You can customize this by passing an options object:
// tailwind.config.js
import headlessuiPlugin from '@headlessui/tailwindcss'
export default {
// ... other config
plugins: [
headlessuiPlugin({ prefix: 'headless' }),
],
}With a custom prefix, you would use classes like headless-open:underline instead of ui-open:underline.
Sources: packages/@headlessui-tailwindcss/src/index.ts
Install the plugin and add it to your Tailwind configuration to enable state-based styling variants for Headless UI components.
npm install @headlessui/tailwindcsstailwind.config.js:module.exports = {
content: [],
theme: {
extend: {},
},
plugins: [
require('@headlessui/tailwindcss')
// Or with a custom prefix:
// require('@headlessui/tailwindcss')({ prefix: 'ui' })
],
}Optionally, pass an options object to require to set a custom prefix for all variants (e.g., { prefix: 'ui' } changes ui-open to ui-open with the specified prefix applied to utility classes if needed, though the variants themselves remain ui-* by default).
npm install @headlessui/tailwindcss
// tailwind.config.js
module.exports = {
content: [],
theme: {
extend: {},
},
plugins: [
require('@headlessui/tailwindcss')
],
}Sources: packages/@headlessui-tailwindcss/README.md
Wrap your Switch components inside a SwitchGroup. The group automatically handles the labelledby and describedby ARIA attributes for child switches.
<script setup>
import { Switch, SwitchGroup, SwitchLabel, SwitchDescription } from '@headlessui/vue'
const switch1 = false
const switch2 = true
</script>
<template>
<SwitchGroup>
<SwitchLabel>Notification Settings</SwitchLabel>
<SwitchDescription>
Manage how you receive notifications.
</SwitchDescription>
<Switch v-model="switch1" class="...">
Email Notifications
</Switch>
<Switch v-model="switch2" class="...">
Push Notifications
</Switch>
</SwitchGroup>
</template>A fix ensures that blurring the Combobox.Input component now correctly closes the Combobox. This improves the user experience by automatically closing the dropdown when the user clicks away from the input field.
Behavior:
Combobox.Input loses focus, the Combobox will close.Usage:
No additional configuration is needed. The fix is applied automatically when using the Combobox component.
import { Combobox, ComboboxInput } from '@headlessui/react'
function MyCombobox() {
return (
<Combobox>
<ComboboxInput />
<ComboboxOptions>
{/* Options */}
</ComboboxOptions>
</Combobox>
)
}import { Combobox, ComboboxInput } from '@headlessui/react'
function MyCombobox() {
return (
<Combobox>
<ComboboxInput />
<ComboboxOptions>
{/* Options */}
</ComboboxOptions>
</Combobox>
)
}Sources: packages/@headlessui-react/CHANGELOG.md
The Combobox component now supports advanced configuration props for better control over behavior and rendering.
New Props:
immediate: When set to true, the Combobox opens immediately when the input receives focus, without requiring a click.virtual: Enables virtualization mode for large lists, allowing you to render only visible options.anchor: Allows you to specify a custom anchor element for the dropdown panel.Usage:
import { Combobox } from '@headlessui/react'
function MyCombobox() {
return (
<Combobox
immediate={true}
virtual={true}
anchor={document.getElementById('custom-anchor')}
>
{/* Your Combobox content */}
</Combobox>
)
}import { Combobox } from '@headlessui/react'
function MyCombobox() {
return (
<Combobox
immediate={true}
virtual={true}
anchor={document.getElementById('custom-anchor')}
>
{/* Your Combobox content */}
</Combobox>
)
}Sources: packages/@headlessui-react/CHANGELOG.md