Appearance
Headless select / combobox comparison
How @rozie-ui/listbox compares to the existing headless select & combobox libraries across the six frameworks. Unlike Rozie's engine-wrapper components (Embla, Flatpickr, Chart.js…), the listbox has no vanilla-JS engine behind it; there is no shared "select core" the way Embla is a shared carousel core. Instead, every framework ecosystem reimplements the WAI-ARIA listbox / combobox pattern from scratch, in its own idiom. The result is the most fragmented landscape of any component Rozie ships: the best options are React-only, the one cross-framework incumbent (Headless UI) covers just React + Vue, Svelte and Solid each have their own separate community libraries, Angular gives you low-level CDK primitives you assemble yourself, and web components have no headless combobox at all. Rozie ships the same headless <Listbox>, with the same props, events, two-way value, slots, and handle, to all six frameworks. (Type-to-filter is the sibling @rozie-ui/combobox family, built on the same headless list spine.)
Research snapshot: 2026-08-10. The headless-UI landscape moves quickly; treat the library names, framework coverage, and "combobox?" column as of that date.
The libraries at a glance
| Framework | Representative headless option(s) | Shape | Listbox | Combobox | Notes |
|---|---|---|---|---|---|
| React | Headless UI, Radix UI, Ariakit, React Aria, downshift | render-props / hooks | ✅ | ✅ (most) | Deepest ecosystem by far. React Aria is the accessibility gold standard; Radix has no combobox primitive; each library is a different API. |
| Vue | Headless UI (Vue), Reka UI, Ark UI | components | ✅ | ✅ | Headless UI ships an official Vue port; Reka UI / Ark UI add more. Solid coverage, fewer choices than React. |
| Svelte | Melt UI, Bits UI | builders / actions | ✅ | ✅ | A separate community ecosystem with no Headless UI port. Healthy, but a different mental model (builders) again. |
| Solid | Kobalte, Ark UI (Solid), Corvu | components | ✅ | ✅ | Community libraries; no first-party headless suite. |
| Angular | Angular CDK (@angular/cdk/listbox), @angular/aria | directives / primitives | ⚠️ primitive | ⚠️ experimental | CDK gives you a low-level listbox primitive; you assemble the popup, filtering, and combobox wiring yourself. The CDK combobox is experimental; Angular 21's new @angular/aria adds first-party headless listbox/combobox directives (developer preview, beyond this project's Angular 19 floor). Material's mat-select/mat-autocomplete are styled, not headless. |
| Lit / web components | (none headless) | — | ❌ | ❌ | No headless select/combobox primitive. Shoelace (archived March 2026 in favour of Web Awesome) <sl-select> / Spectrum are styled components, not headless behaviour you can re-skin. You hand-roll the ARIA. |
| Rozie | @rozie-ui/listbox-* | a component | ✅ | ✅ via @rozie-ui/combobox | Same API on all six: props, events, two-way value, slots, handle. Select-only by design; type-to-filter is the sibling @rozie-ui/combobox family. |
On its home framework each of these is a solid pick. The case for Rozie is consistency and coverage: the cross-framework incumbent (Headless UI) stops at React + Vue; the deepest options (Radix, Ariakit, React Aria, downshift) are React-only; Svelte and Solid are separate ecosystems with separate APIs; Angular hands you primitives, not an assembled control; and Lit / web components have nothing headless. A team shipping a cross-framework design system today maintains a render-prop component on React, a builder on Svelte, a CDK assembly on Angular, and a hand-rolled web component for Lit: four mental models and two gaps for the same ARIA pattern. Rozie gives them all the same <Listbox>.
Feature matrix
Cell legend: ✅ = documented out-of-the-box · ❌ = not supported / not present · ⚠️ = partial / consumer-assembly-required.
| Capability | React (HUI/Radix/Aria…) | Vue (HUI/Reka) | Svelte (Melt/Bits) | Solid (Kobalte) | Angular (CDK) | Lit (none) | @rozie-ui/listbox |
|---|---|---|---|---|---|---|---|
| Headless ARIA listbox | ✅ | ✅ | ✅ | ✅ | ⚠️ primitive | ❌ | ✅ |
| Combobox (type-to-filter) | ✅ (✗ Radix) | ✅ | ✅ | ✅ | ⚠️ experimental | ❌ | ✅ via @rozie-ui/combobox |
| Idiomatic component surface | ⚠️ render-props/hooks | ✅ | ⚠️ builders | ✅ | ⚠️ directives | hand-roll | ✅ <Listbox> |
| Single and multi select | ✅ | ✅ | ✅ | ✅ | ⚠️ | — | ✅ multiple |
| Two-way value binding | ⚠️ value+onChange | ✅ v-model | ⚠️ bind store | ⚠️ signal | ⚠️ wire CVA | — | ✅ r-model:value (Angular CVA) |
| Type-ahead (printable keys) | ✅ | ✅ | ✅ | ✅ | ⚠️ | — | ✅ |
| Client filter + remote hook | ✅ | ✅ | ✅ | ✅ | ⚠️ | — | via @rozie-ui/combobox |
| Scoped option rendering | ✅ render-prop | ✅ slot | ✅ snippet | ✅ | ⚠️ template | — | ✅ option / selected / empty slots |
| Windowing for long lists | ✅ (wire a virtualizer) | ⚠️ | ⚠️ | ⚠️ | ⚠️ | — | ✅ opt-in virtual (virtual-core, ×6) |
| Imperative handle | ⚠️ varies | ⚠️ varies | ⚠️ varies | ⚠️ varies | ⚠️ | hand-roll | ✅ uniform 5-verb $expose |
| Zero-config styling, re-skinnable | ⚠️ unstyled, wire it | ⚠️ | ⚠️ | ⚠️ | ⚠️ | — | ✅ CSS-var tokens + shadcn/Material/Bootstrap bridges |
| Same API on all 6 frameworks | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
Where Rozie wins today
- First-class packages everywhere — including Lit / web components, which have no headless select to begin with, and Angular, which gets a fully-assembled listbox instead of the CDK's lower-level primitives. Both are categories the incumbents simply don't serve.
- The same component surface everywhere. Where the ecosystem offers render-props (React), components (Vue/Solid), builders (Svelte), and directives (Angular),
@rozie-ui/listboxis one<Listbox>with the same props, events, two-wayvalue, slots, and handle. One component to learn, document, and migrate across your stack. - The select-only pattern, done fully. The control follows the ARIA APG select-only combobox pattern: DOM focus stays on the control while the highlighted option is tracked virtually via
aria-activedescendant, with full keyboard navigation and type-ahead in the box. Type-to-filter with an editable input is deliberately a sibling family,@rozie-ui/combobox, sharing the same headless list spine. - A real two-way
value.r-model:valuereads and writes the selection with noonChange → setStateglue. Becausevalueis the solemodel: trueprop, the Angular output additionally implementsControlValueAccessor, so aListboxis a form control ([formControl]/[(ngModel)]bind directly). - Single + multi select and type-ahead out of the box: flip
multipleand get array values, identical on every target. - Zero-config styling that re-skins to any design system. Every rendered value is a
--rozie-listbox-*CSS custom property with a built-in fallback, plus ready-made token bridges for shadcn/ui, Material 3, and Bootstrap 5. Most headless libraries are unstyled-and-you-wire-every-element; Rozie works on drop-in yet stays fully re-skinnable.
What Rozie defers
- React Aria's accessibility depth. React Aria is the gold standard: locale-aware type-ahead collation, RTL, exhaustive touch / pointer / virtual-cursor handling, and screen-reader testing across a large browser × AT matrix.
@rozie-ui/listboximplements the APG patterns faithfully and is gate-verified, but it does not match the breadth of React Aria's edge-case coverage. On React specifically, React Aria is the right call when that depth is the priority. - It's a single component, not a primitive suite. Headless UI, Radix, Ariakit, Kobalte, and Melt ship whole families: Menu, Dialog, Tabs, Popover, Tooltip, and more. Rozie ships the listbox (alongside its other
@rozie-uicomponents), not a unified headless-primitive system. - Deep virtualization edge cases. React Aria and others integrate full-featured list virtualizers (variable heights, sticky sections, horizontal scroll).
@rozie-ui/listboxships opt-in vertical windowing for long lists via:virtual(the same@tanstack/virtual-coreengine the@rozie-ui/data-tablefamily uses, wired in cross-framework), so only the visible slice renders, backed by behavioral specs. It does not yet cover the more exotic virtualization modes. - Batteries-included async data. downshift and React Aria have mature async/remote-data patterns (loading states, debounce, cancellation). Rozie's type-to-filter and remote-data story lives in the sibling
@rozie-ui/comboboxfamily; the select-onlyListboxdoes not model async loading. @rozie-ui/listboxis pre-1.0 and younger and less battle-tested than the established libraries. The full surface is documented in the showcase & API.
Try it
The @rozie-ui/listbox showcase + API reference documents the @rozie-ui/listbox-* packages: one pre-compiled, per-framework install (npm i @rozie-ui/listbox-react, etc.). There is no engine to import and no required CSS: the ARIA behaviour and a fully-tokenised skin ship inside the component, with optional one-line theme bridges for shadcn/ui, Material 3, and Bootstrap 5. The live demo runs the real Vue package in the page.
Cross-references
- Listbox — showcase & API — the full
@rozie-ui/listboxsurface, quick start, theming, keyboard, and accessibility reference. - Listbox — live demo — the real Vue package running in the page, plus the Rozie source and the six generated outputs.
Listbox.roziesource on GitHub- Combobox libraries comparison — the sibling type-to-filter family.
- Embla libraries comparison — a sibling port, contrasting the engine-wrapper story with this headless-behaviour one.