Library / SDK
downshift-js/downshift avatar
downshift-js/downshift

downshift: WAI-ARIA Primitives for React Combobox and Select Components

🏎 A set of primitives to build simple, flexible, WAI-ARIA compliant React autocomplete, combobox or select dropdown components.

12,303 stars935 forksJavaScriptMIT

At a glance

What is it?
downshift is a JavaScript library that provides React hooks and a render-prop component for building accessible combobox, select, and autocomplete UI components. It handles ARIA attributes, keyboard navigation, and state management without dictating markup or visual design.
Who is it for?
downshift is the right library when you need accessible combobox or select components in React and want complete control over the markup. Use `useCombobox` for autocomplete inputs and `useSelect` for custom select dropdowns; both follow the ARIA 1.2 combobox pattern.
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?
Yes. The repository last received commits 92 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

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

Editorial analysis

The Accessibility Problem downshift Solves

Building a combobox or autocomplete input that works correctly for keyboard-only users and screen reader users requires implementing a specific ARIA pattern: correct roles, states, and properties on the input, the listbox, and each option, plus keyboard interactions that match the W3C combobox design pattern. Most custom implementations get parts of this wrong, either by using incorrect ARIA roles or by missing keyboard interactions such as Escape to close, Arrow keys to navigate, and Home and End to jump to list boundaries.

downshift provides the stateful logic as React hooks. You write the markup and apply prop getter functions returned by the hooks to your elements. The hooks calculate the correct ARIA attributes and keyboard event handlers and return them as objects you spread onto your DOM elements. The library does not render any markup itself.

The README states the problem directly: the library exists because you need an autocomplete or select experience that is accessible, simple, and flexible for your use cases, while following the ARIA design pattern for a combobox or select.

Three Hooks: useCombobox, useSelect, and useTagGroup

The library offers three hooks. `useCombobox` is for combobox or autocomplete inputs where the user types to filter options. `useSelect` is for custom select dropdowns where the user picks from a list without typing. `useTagGroup` is for tag groups, useful for building multiple-selection components where chosen items appear as tags.

Both `useCombobox` and `useSelect` implement the ARIA 1.2 combobox pattern. The README states explicitly that the older `Downshift` render-prop component does not support the latest ARIA patterns and that new code should use the hooks. The `Downshift` component will be removed once the hooks cover its use cases.

Version 7 migrated the hooks to ARIA 1.2. A migration guide named `Migration Guide v7` is linked from the README and documents the specific behaviour changes introduced in that version.

Installing downshift and Building a First Combobox

downshift is distributed on npm:

code
npm install --save downshift

The README notes that React must be installed separately. The library also supports Preact out of the box; the Preact module is available at `downshift/preact` via an import such as `import Downshift from 'downshift/preact'`.

The `useCombobox` hook accepts a configuration object with properties such as `items`, `onInputValueChange`, and `onSelectedItemChange`. It returns prop getter functions: `getInputProps`, `getMenuProps`, `getItemProps`, `getLabelProps`, and `getToggleButtonProps`. Each getter returns an object of props you spread onto the corresponding DOM element. The hook manages open/closed state, highlighted index, and input value internally. State properties like `isOpen` and `highlightedIndex` are returned alongside the getters so you can control rendering.

The pattern separates logic from presentation completely. You write a list element with whatever classes and styles your design requires, call `getItemProps({ item, index })` on it, and the hook ensures the correct ARIA role, aria-selected, and event handlers are applied. The caller is responsible for all markup; the hook is responsible for all accessibility behaviour.

Control Props, State Reducers, and Custom Behaviour

downshift supports controlled usage through control props. You can pass `selectedItem`, `inputValue`, `isOpen`, or `highlightedIndex` directly to the hook to override internal state. This is the standard React pattern for controlled inputs applied to a compound component.

The `stateReducer` prop is the escape hatch for customising state transitions. It is a function that receives the current state, the action being performed, and the proposed next state, and returns the state to actually apply. The README documents it as the mechanism for cases such as keeping the menu open after an item is selected or preventing the input from clearing on close.

The `itemToString` prop controls how a selected item is converted to a string for the input value. The `onStateChange` callback fires after any state change and receives an object describing what changed. `onSelect`, `onChange`, and `onInputValueChange` are more focused callbacks for common reactions.

The `id` prop and its companions `inputId`, `labelId`, `menuId`, and `getItemId` allow overriding the generated ARIA IDs. This is important in server-side rendering contexts where randomly generated IDs on the server and client can mismatch, breaking hydration. The README lists all of these in its Basic Props and Advanced Props sections.

The `stateChangeTypes` export enumerates all the action types that can appear in the state reducer, such as `__item_click__`, `__input_keydown_arrow_up__`, or `__toggle_button_click__`. Callers who need to intercept a specific interaction pattern can check the action type in the reducer and return a modified state only for that case.

React Native Support and Its Constraints

The README documents React Native support with a dedicated section. The `react-native` field in package.json points to `dist/downshift.native.cjs.cjs`, which is a build that excludes web-specific DOM APIs.

The README lists specific gotchas for React Native. The `environment` prop must be set in certain configurations. The accessibility tree in React Native does not map 1:1 to ARIA roles, so the guarantees that the web hooks provide for screen readers do not fully transfer to mobile screen readers. The package.json build script generates separate `.native.cjs.cjs` and `.nativeweb.cjs.cjs` outputs, the latter for React Native Web.

The library package.json notes that `sideEffects` is false, meaning bundlers can tree-shake it safely. The bundle formats distributed are UMD, CJS, and ESM.

Maintenance Status and the Legacy Render-Prop Component

The most recent release was v9.4.0 on 2026-06-30. Prior releases were v9.3.6 and v9.3.5, both on 2026-06-04. The last push to the repository was on 2026-06-30.

The `Downshift` render-prop component is still in the package but is flagged in the README with prominent warnings to use the hooks instead. The README states that `Downshift` does not support the latest ARIA combobox patterns and that it will be removed once the hooks become mature enough to cover all its use cases. Adopting it in new code creates a migration debt.

Tailwind UI and headless UI are alternatives for teams that want accessible React components with more opinionated design tokens. Headless UI, maintained by the Tailwind team, provides a similar unstyled, accessible component set. The key difference is that headless UI renders its own DOM elements while downshift provides only prop getters and state, leaving the complete DOM to the caller.

Editorial conclusion

downshift is the right library when you need accessible combobox or select components in React and want complete control over the markup. Use `useCombobox` for autocomplete inputs and `useSelect` for custom select dropdowns; both follow the ARIA 1.2 combobox pattern. The legacy `Downshift` render-prop component is still present but the README explicitly states it will be removed once the hooks are mature. Do not use the render-prop component in new code. Check the v7 migration guide before upgrading from any version below 7.0, as the ARIA pattern change introduced in that version affects how keyboard navigation behaves for screen reader users.

Frequently asked questions

What is the difference between useCombobox and useSelect in downshift?

useCombobox is for autocomplete inputs where the user types to filter options. useSelect is for custom select dropdowns where the user picks from a list without a text input. Both implement the WAI-ARIA 1.2 combobox pattern.

Should I use the Downshift component or the hooks in new code?

Use the hooks. The README explicitly recommends useCombobox and useSelect for new code and states that the Downshift render-prop component does not support the latest ARIA patterns and will be removed once the hooks cover its use cases.

Does downshift work with Preact?

Yes. The README states that the library supports Preact out of the box. The Preact module is available as a named import from `downshift/preact`.

Official sources

  1. downshift-js/downshift on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
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/downshift-js-downshift.svg)](https://hysenlabs.com/projects/downshift-js-downshift)