Skip to content

hardisp/hrdi-select-searchable

Repository files navigation

Searchable Select (React)

A Searchable, accessible Select / Combobox for React, built with TypeScript, Floating UI, and WAI-ARIA best practices.

This library provides logic and accessibility, not styles.

You bring your own UI.

Features

  • ✅ Headless architecture (logic ≠ UI)
  • ✅ Single & multi-select
  • ✅ Searchable options
  • ✅ ARIA compliant combobox / listbox
  • ✅ Portal support (optional)
  • ✅ Floating positioning with collision handling
  • ✅ Z-index safe
  • ✅ Mouse + keyboard synced
  • ✅ Screen-reader friendly

Installation

npm install @hrdi/searchable-select

This package assumes React 18+ and TypeScript.

🚀 Basic Usage

<SelectRoot
  options={options}
  value={value}
  onChange={setValue}
>
  <SelectTrigger>
    <SelectValue />
  </SelectTrigger>

  <SelectDropdown>
    {options.map((opt, i) => (
      <SelectOption key={opt.value} option={opt} index={i} />
    ))}
  </SelectDropdown>
</SelectRoot>

🔢 Multi-Select with Search

<SelectRoot
  options={options}
  multiple
  searchable
  value={value}
  onChange={setValue}
>
  <SelectTrigger>
    <SelectValue placeholder="Select items..." />
  </SelectTrigger>

  <SelectDropdown portal>
    <SelectSearch />
    {options.map((opt, i) => (
      <SelectOption key={opt.value} option={opt} index={i} />
    ))}
  </SelectDropdown>
</SelectRoot>

API Reference

SelectRoot

export type SelectOptionProps = {
  value: string;
  label: string;
};

export type UseSelectProps = {
  options: SelectOptionProps[];
  value?: SelectOptionProps | SelectOptionProps[];
  multiple?: boolean;
  searchable?: boolean;
  filterFn?: (opt: SelectOptionProps, search: string) => boolean;
  onChange: (value: SelectOptionProps | SelectOptionProps[]) => void;
};

export type FloatingPlacement = "bottom-start" | "bottom-end";

SelectTrigger

Acts as the combobox trigger.

  • Handles keyboard interaction
  • Holds focus
  • Controls open/close state
<SelectTrigger>
  <SelectValue />
</SelectTrigger>

ARIA:

  • role="combobox"
  • aria-expanded
  • aria-activedescendant

SelectValue

Renders the selected value(s).

  • Single select → label
  • Multi select → chips with remove buttons
<SelectValue placeholder="Select..." />

SelectDropdown

Renders the floating listbox.

<SelectDropdown portal>
  ...
</SelectDropdown>

Behavior:

  • Auto-focuses when dropdown opens
  • Filters options via filterFn

ARIA:

  • role="textbox"
  • aria-autocomplete="list"

SelectOption

Renders an individual option.

Props:

  • option: SelectOption
  • index: number

ARIA:

  • role="option"
  • aria-selected

Accessibility (ARIA)

This component follows WAI-ARIA Combobox with Listbox pattern.

Implemented:

  • role="combobox"
  • role="listbox"
  • role="option"
  • aria-expanded
  • aria-selected
  • aria-activedescendant
  • Meets WCAG 2.1 AA expectations.

Portal & Positioning

  • Uses Floating UI
  • Auto-flips on viewport edge
  • Scroll & resize aware
  • Works with any z-index
<SelectDropdown portal />

Disable portal if needed:

<SelectDropdown portal={false} />

Styling

This library is unstyled by design.

Use:

  • CSS
  • Tailwind
  • Styled Components
  • Vanilla Extract
  • Any design system

Example:

[data-highlighted="true"] {
  background: #eef2ff;
}

[aria-selected="true"] {
  font-weight: 600;
}

🧪 Testing

Because logic is headless:

  • useSelect can be unit tested
  • UI primitives can be tested independently
  • No DOM-dependent state machines

❌ Non-Goals

  • ❌ No built-in styles
  • ❌ No opinionated UI
  • ❌ No framework lock-in

License

MIT (or internal use)

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors