Library / SDK
tailwindlabs/headlessui avatar
tailwindlabs/headlessui

Headless UI's insiders tag warns that its own APIs can still change, and its releases go quiet for months

Completely unstyled, fully accessible UI components, designed to integrate beautifully with Tailwind CSS.

28,829 stars1,218 forksTypeScriptMIT

At a glance

What is it?
Headless UI is a set of completely unstyled, fully accessible UI components for React and Vue, published as three separate npm packages with an MIT license. What the README and root manifest do tell you is that the insiders build does not follow semver and does not guarantee stable APIs, that the root manifest is private and pinned at 0.0.0, and that the test and lint gates live in shell scripts rather than in the manifest.
Who is it for?
Headless UI suits a React or Vue project that wants accessible behaviour without opinionated markup, and that can read the component docs on headlessui.com rather than expecting the repository to explain them. It is a poor fit if you need a predictable release rhythm, since the last three React tags span September 2025 to April 2026, and a poor fit if your build depends on the insiders channel, which the project itself says may change API at release time.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Activity is slowing. The repository last received commits 7 months ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • tailwindlabs/headlessui28,829 ★ · +69
Star history
Repository2026-09-282026-11-14
tailwindlabs/headlessui28.8k28.8k

69 Stars in the last 47 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

The insiders tag points at main and the project warns its APIs can change

The README publishes two install routes and is explicit about the difference. The stable route is `npm install @headlessui/react@latest` or `npm install @headlessui/vue@latest`. The other is the insiders route, `npm install @headlessui/react@insiders` or `npm install @headlessui/vue@insiders`, which is described as pointing to whatever the latest commit on the main branch is, followed by a note that the insiders build doesn't follow semver and therefore doesn't guarantee that the APIs will be the same once they are released. That is the project telling you, in its own words, that a green build on insiders is not evidence for the release. The consequence is twofold. A team that prototypes on insiders faces an API change at release time, and a dependency that tracks the insiders tag resolves to whatever main is at install time, so two developers installing at different times can be on different code.

Three packages ship, and the version you install is not printed in the README

Headless UI is three npm packages, not one. The package table lists @headlessui/react, @headlessui/vue, and @headlessui/tailwindcss, and the first two are what the install commands target while the third is the Tailwind integration. A React project takes the first, a Vue project takes the second, and a Tailwind setup has a reason to take the third, so the name Headless UI on its own does not tell you which artifact you need. The version and downloads columns in that table are not numbers either; each cell is a link out to the package's page on npmjs.com. The consequence is that the repository does not state a version for you, and you have to read npm or the per-package manifest. The release list reinforces the split, since the three most recent tags are all @headlessui/react releases, so the Vue line has its own schedule you will not see from the React tags.

The root manifest is private and pinned at 0.0.0, so versions live per package

The root package.json is a workspace shell, not a publishable artifact. It declares the name headlessui, the version 0.0.0, and private set to true, with main pointing at index.js, and it declares workspaces as packages/* and playgrounds/*. Two things follow from those five lines. The 0.0.0 is not a stale version of the library, it is a placeholder on a package that is never published, so any check that reads the root version is reading nothing useful. And the workspaces array is what tells you the repository is a monorepo with two kinds of member, the published packages under packages/ and the demo applications under playgrounds/, which is also why the manifest carries a prettier override for each playground. The practical consequence is that the version you depend on lives in your own lockfile, and reviewing an upgrade means checking the release for the specific package you use.

Test and lint live in shell scripts, and CI=true changes what lint does

The interesting scripts in the root manifest are the ones that hand off to files. The test script is `./scripts/test.sh` and the lint script is `./scripts/lint.sh`, so the actual gates are not described anywhere in package.json. Two variants exist for linting: `lint` runs the script directly, while `lint-check` prefixes it with CI=true, and `lint-types` similarly sets CI=true before running a type lint across the workspaces with --if-present. So the same script has two documented behaviours, and which one you get depends on the environment variable rather than on a flag. The consequence for a contributor is that you cannot answer the question what does this project check by reading the manifest. You have to open scripts/test.sh and scripts/lint.sh, and if you are trying to reproduce a CI failure locally, setting CI=true is part of the reproduction.

The pre-commit hook maps every staged path to the whole lint script

Git hooks are configured in the manifest, with husky running lint-staged on pre-commit, and the lint-staged configuration is a single line:

code
  "lint-staged": {
    "*": "npm run lint"
  }

The pattern matches every staged file and routes it to npm run lint, the shell script from the previous section, so what a commit checks depends on what scripts/lint.sh does with the files it is given. The parallel command exists, since `release-channel` runs node ./scripts/release-channel.js, which is presumably what produces the insiders channel referenced in the install instructions.

SWC compiles, Jest tests, and Prettier needs to be told where your stylesheet is

The toolchain is visible in the root entries. A .swcrc is the compiler configuration, so the build runs through SWC, and jest.config.cjs with a jest/ directory is the test runner, with a package-lock.json showing npm workspaces rather than another package manager. Formatting is Prettier, configured inside package.json with printWidth 100, semi false, singleQuote true, and trailingComma es5, and there are both .prettierignore and .eslintignore entries. Two Prettier plugins are loaded, one that organises imports and one for Tailwind. The second one needs to know which stylesheet defines your classes, and the config handles that with overrides: for files under ./playgrounds/react it sets tailwindStylesheet to ./playgrounds/react/pages/styles.css, with a matching override for the Vue playground. The consequence is that a new app added to the workspaces has to be registered in an override or class ordering is checked against the wrong file.

Two React tags in September 2025, then a six-month gap

The release list is @headlessui/react at v2.2.8 on 2025-09-12, v2.2.9 on 2025-09-25, and v2.2.10 on 2026-04-07. Two patch releases thirteen days apart, then nothing for about six and a half months, then a single patch. The last push to the main branch is dated 2026-04-13, six days after that newest tag. The repository is not archived, so the work is not finished, but the cadence is irregular enough that you should not plan around frequent patches, and a six-month gap that yields one patch release is worth noticing before you file an issue against an old version. Support is one channel, GitHub Discussions, described as the place for help, best-practice discussion, and feature ideas, and the README asks that you read the contributing docs at .github/CONTRIBUTING.md before submitting a pull request.

Editorial conclusion

Headless UI suits a React or Vue project that wants accessible behaviour without opinionated markup, and that can read the component docs on headlessui.com rather than expecting the repository to explain them. It is a poor fit if you need a predictable release rhythm, since the last three React tags span September 2025 to April 2026, and a poor fit if your build depends on the insiders channel, which the project itself says may change API at release time. Before you commit, decide which of the three packages you actually need, pin an exact version rather than tracking a tag, and read the contributing docs before a pull request rather than after it.

Frequently asked questions

how to install headlessui react

Run `npm install @headlessui/react@latest` for the current release, or `npm install @headlessui/react@insiders` for a build that points at whatever the latest commit on the main branch is. The insiders build does not follow semver and the README says it does not guarantee the APIs will be the same once released.

how to install headlessui

Pick the package that matches your framework: `npm install @headlessui/react@latest` for React, `npm install @headlessui/vue@latest` for Vue, and @headlessui/tailwindcss is a separate package for the Tailwind integration. Full documentation is at headlessui.com.

what is headlessui react

It is the React package of Headless UI, a set of completely unstyled, fully accessible UI components designed to integrate with Tailwind CSS. The package is MIT licensed and lives in the packages/ workspace of the monorepo.

How does Headless UI work?

The repository describes what the components are, not how they are built: a set of completely unstyled, fully accessible UI components designed to integrate with Tailwind CSS, shipped as three packages. The component documentation and any description of the internals live on headlessui.com, not in the README.

What are the key differences between Headless UI and Radix UI?

The README makes no comparison with Radix UI or with any other library. Its own scope is stated as completely unstyled, fully accessible UI components designed to integrate with Tailwind CSS, published as @headlessui/react, @headlessui/vue, and @headlessui/tailwindcss.

headlessui alternative

The README names no alternatives. For help, discussion about best practices, or feature ideas it points to the project's GitHub Discussions, and for anything deeper it asks that you read the contributing docs before opening a pull request.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. tailwindlabs/headlessui on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/tailwindlabs-headlessui.svg)](https://hysenlabs.com/projects/tailwindlabs-headlessui)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/tailwindlabs-headlessui.svg)](https://hysenlabs.com/projects/tailwindlabs-headlessui)

Was this useful?