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 authors the ARIA behaviour once and ships it to all six.
Research snapshot: 2026-06-16. 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 — 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) | 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. Material's mat-select/mat-autocomplete are styled, not headless. |
| Lit / web components | (none headless) | — | ❌ | ❌ | No headless select/combobox primitive. Shoelace <sl-select> / Spectrum are styled components, not headless behaviour you can re-skin. You hand-roll the ARIA. |
| Rozie | @rozie-ui/listbox-* | a component | ✅ | ✅ | One source → all six, same props / events / two-way value / slots / handle. Listbox and combobox in one component (a combobox boolean flip). |
These libraries are excellent — on its home framework each is the obvious pick, and Rozie does not claim to out-feature React Aria on React or Melt UI on Svelte. The wedge 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 combobox; 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 all six the same idiomatic <Listbox> from one definition.
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 | ❌ | ✅ (combobox flag) |
| 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 | ✅ | ✅ | ✅ | ✅ | ⚠️ | — | ✅ filterable + search event |
| 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 |
| One source → all 6 frameworks | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
Where Rozie wins today
- One definition, six idiomatic packages — including Lit / web components, which have no headless select/combobox to begin with, and Angular, which gets a fully-assembled combobox 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) — four mental models —
@rozie-ui/listboxis one<Listbox>with the same props, events, two-wayvalue, slots, and handle on all six. - Listbox and combobox in one component.
comboboxis a single boolean: off → an ARIA select-only button trigger; on → arole="combobox"text input that filters as you type. Several ecosystems split these into separate primitives, and Radix has no combobox at all. - A real two-way
valueon all six —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, type-ahead, and a remote-filter hook out of the box — flip
multiple, get array values; flipfilterable="false"and listen tosearchto drive server-side filtering — 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
This page concedes where the incumbents are genuinely ahead — that's what keeps the comparison credible, and it doubles as Rozie's own roadmap.
- 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 across all six targets, 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 listbox/combobox (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) — only the visible slice renders, backed by behavioral specs across all six targets — but it does not yet cover the more exotic virtualization modes. - Batteries-included async combobox. downshift and React Aria have mature async/remote-data patterns (loading states, debounce, cancellation). Rozie gives you the
searchevent andfilterable="false"hook; the loading/debounce/cancellation policy is yours to wire. @rozie-ui/listboxis0.1.0. The surface (13 props / 3 events / single + multi / combobox / 5-verb handle / 3 slots) is stable and gate-verified across all six targets, but it is younger and less battle-tested than the established libraries.
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 (select + combobox), plus the one
.roziesource and all six generated outputs. Listbox.roziesource on GitHub- Embla libraries comparison — a sibling port, contrasting the engine-wrapper story with this headless-behaviour one.