@react-aria/listbox vs downshift vs react-autocomplete vs react-select
React Autocomplete and Select Component Libraries
@react-aria/listboxdownshiftreact-autocompletereact-selectSimilar Packages:

React Autocomplete and Select Component Libraries

@react-aria/listbox, downshift, react-autocomplete, and react-select are libraries that help developers build accessible, interactive selection UIs like autocomplete fields, dropdowns, and listboxes in React applications. They each provide varying levels of abstraction, customization, and built-in features to handle keyboard navigation, ARIA roles, focus management, and user interactions according to accessibility standards.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
@react-aria/listbox3,196,33915,04420.3 kB58316 days agoApache-2.0
downshift012,2963.01 MB622 months agoMIT
react-autocomplete02,162-918 years agoMIT
react-select028,042726 kB48710 months agoMIT

Building Accessible Selection UIs in React: @react-aria/listbox vs downshift vs react-autocomplete vs react-select

When you need to add an autocomplete, dropdown, or listbox to a React app, you quickly realize how much complexity lies beneath a seemingly simple UI: keyboard navigation, screen reader announcements, focus trapping, option filtering, and more. The four packages compared here offer different approaches to solving this problem — from full-featured components to bare-metal hooks. Let’s examine how they differ in practice.

⚠️ Deprecation Alert: react-autocomplete Is No Longer Maintained

Before diving into comparisons, note that react-autocomplete is deprecated. Its npm page states: "This project is no longer maintained. Please consider using downshift instead." It hasn’t seen meaningful updates since 2018 and doesn’t support modern React patterns like hooks or concurrent rendering. Do not use it in new projects. We include it only for awareness and migration context.

// ❌ Avoid: react-autocomplete (deprecated)
import Autocomplete from 'react-autocomplete';

// This works but lacks modern accessibility fixes and TypeScript support
<Autocomplete
  items={items}
  renderItem={(item, isHighlighted) => (
    <div style={{ background: isHighlighted ? 'lightgray' : 'white' }}>
      {item.label}
    </div>
  )}
  value={value}
  onChange={setValue}
  onSelect={setValue}
/>

Now, let’s compare the three viable options.

🧱 Architecture: Headless Hook vs Unstyled Primitive vs Complete Component

@react-aria/listbox: Unstyled ARIA Primitive

@react-aria/listbox is part of the React Aria ecosystem from Adobe. It provides low-level hooks that implement ARIA design patterns but render no DOM elements themselves. You must build the entire UI — input, list container, options — and connect them using the hook’s returned props.

import { useListState } from '@react-stately/list';
import { useListBox, useOption } from '@react-aria/listbox';

function ListBox({ options }) {
  const state = useListState({ items: options });
  const ref = useRef();
  const { listBoxProps } = useListBox({ ...state }, state, ref);

  return (
    <ul {...listBoxProps} ref={ref}>
      {[...state.collection].map(item => (
        <Option key={item.key} item={item} state={state} />
      ))}
    </ul>
  );
}

function Option({ item, state }) {
  const ref = useRef();
  const { optionProps } = useOption({ key: item.key }, state, ref);

  return (
    <li {...optionProps} ref={ref}>
      {item.rendered}
    </li>
  );
}

You’ll also need separate hooks like useComboBox or useSelect if you want an input field or trigger button. This modularity is powerful but requires significant setup.

downshift: Headless Interaction Engine

downshift gives you a render-prop or hook-based API that manages selection state, keyboard events, and ARIA attributes, but expects you to render everything. It’s framework-agnostic in spirit and works well with any styling solution.

import { useCombobox } from 'downshift';

function Autocomplete({ items }) {
  const {
    isOpen,
    getMenuProps,
    getInputProps,
    getToggleButtonProps,
    getItemProps,
    highlightedIndex
  } = useCombobox({
    items,
    itemToString: item => item?.label || '',
    onInputValueChange: ({ inputValue }) => {
      // Filter items based on inputValue
    }
  });

  return (
    <div>
      <input {...getInputProps()} />
      <button {...getToggleButtonProps()}>▼</button>
      <ul {...getMenuProps()}>
        {isOpen &&
          items.map((item, index) => (
            <li
              {...getItemProps({ item, index })}
              style={{ backgroundColor: highlightedIndex === index ? 'lightblue' : 'white' }}
            >
              {item.label}
            </li>
          ))}
      </ul>
    </div>
  );
}

downshift handles tricky parts like closing the menu on blur, arrow key navigation, and ARIA live regions — but you control every pixel.

react-select: Batteries-Included Component

react-select ships a fully styled, ready-to-use component with sensible defaults. You can customize almost everything via props or theming, but the basic case requires zero extra work.

import Select from 'react-select';

const options = [
  { value: 'chocolate', label: 'Chocolate' },
  { value: 'strawberry', label: 'Strawberry' }
];

// ✅ Works out of the box
<Select options={options} />

// Customize with props
<Select
  isMulti
  isClearable
  isSearchable
  placeholder="Choose..."
  theme={theme => ({
    ...theme,
    borderRadius: 0,
    colors: { ...theme.colors, primary: 'black' }
  })}
/>

It includes built-in support for async loading, creatable options (CreatableSelect), and even virtualized lists for large datasets.

🔌 Integration Effort: From DIY to Drop-In

PackageSetup TimeCustomization EffortLearning Curve
@react-aria/listboxHighVery HighSteep (requires understanding React Aria’s state/hook split)
downshiftMediumHighModerate (you manage rendering logic)
react-selectLowLow–MediumShallow (just pass props)
react-autocompleteLowMediumShallow — but obsolete

If you need a quick solution for an internal tool, react-select wins. If you’re building a design system with unique interaction requirements, @react-aria/listbox or downshift give you the foundation without visual opinions.

♿ Accessibility: Built-In vs Manual Wiring

All three active libraries aim for WCAG compliance, but their approaches differ:

  • @react-aria/listbox: Implements ARIA 1.2 listbox and combobox patterns exactly as specified. You inherit robust semantics if you follow the docs.
  • downshift: Manages ARIA attributes (aria-activedescendant, aria-expanded, etc.) automatically via its prop getters. Correct usage depends on applying all returned props.
  • react-select: Ships with tested, screen-reader-friendly markup out of the box. Handles edge cases like announcement of selected items and live region updates.

However, accessibility is only as good as your implementation. With @react-aria/listbox or downshift, forgetting to spread {...optionProps} breaks keyboard nav. With react-select, you’re safe by default — unless you override critical props.

🎨 Styling and Theming

  • @react-aria/listbox: Zero styles. You bring your own CSS, CSS-in-JS, or utility classes.
  • downshift: Also zero styles. Entirely dependent on your rendering.
  • react-select: Comes with default styles (via Emotion) but supports deep customization through the styles and theme props, or by replacing components entirely via the components prop.
// react-select: Replace only the Option component
<Select
  components={{
    Option: ({ children, ...props }) => (
      <div {...props.innerProps} className="my-custom-option">
        {children}
      </div>
    )
  }}
/>

📦 Feature Comparison: What’s Included?

Feature@react-aria/listboxdownshiftreact-select
Async options❌ (you implement)❌ (you implement)✅ (loadOptions prop)
Multi-select❌ (use useMultipleSelection)✅ (with extra logic)✅ (isMulti prop)
Creatable options✅ (manual)✅ (CreatableSelect)
Virtualized lists✅ (via react-window integration)
Built-in filtering✅ (customizable via filterOption)
TypeScript support

🛠️ When to Use Which

Use @react-aria/listbox when:

  • You’re adopting React Aria’s full ecosystem (e.g., for a design system).
  • You need pixel-perfect control over every element and interaction.
  • Your team has bandwidth to implement and test custom selection logic.

Use downshift when:

  • You want a lightweight, dependency-free core that handles interaction complexity.
  • You already have a UI kit and just need the “brains” for your dropdown.
  • You prefer composition over pre-built components.

Use react-select when:

  • You need a production-ready select with minimal effort.
  • Features like async loading, tagging, or search are required.
  • Consistency across forms matters more than unique styling.

Never use react-autocomplete in new codebases.

💡 Final Thought

These tools exist on a spectrum from foundation (@react-aria/listbox, downshift) to product (react-select). Choose based on whether your priority is control or velocity. And always — always — test with a keyboard and screen reader, no matter which library you pick.

How to Choose: @react-aria/listbox vs downshift vs react-autocomplete vs react-select

  • @react-aria/listbox:

    Choose @react-aria/listbox if you're already using or planning to adopt the full React Aria suite and need a low-level, unstyled component that strictly follows ARIA patterns. It gives you complete control over rendering and styling but requires you to implement layout, filtering, and state management yourself. Best suited for design systems or teams that prioritize strict accessibility compliance and custom UIs.

  • downshift:

    Choose downshift if you want a lightweight, headless library that handles complex interaction logic (like keyboard navigation and ARIA attributes) while leaving all rendering and styling up to you. It’s ideal when you need maximum flexibility without opinionated markup or styles, and you’re comfortable wiring up your own input, list, and item components.

  • react-autocomplete:

    Do not choose react-autocomplete for new projects — it is officially deprecated as noted on its npm page. The package hasn’t been updated in years and lacks modern React features, accessibility improvements, and active maintenance. Migrate existing usage to alternatives like downshift or react-select.

  • react-select:

    Choose react-select if you need a fully-featured, production-ready select component with built-in support for async options, multi-select, creatable options, extensive theming, and accessibility. It’s best when you want to ship quickly with minimal custom implementation, especially in admin panels, forms, or internal tools where consistent UX outweighs pixel-perfect design control.

README for @react-aria/listbox

@react-aria/listbox

This package is part of react-spectrum. See the repo for more details.