Select
react-select is what people usually mean when they ask for "Select2 in React":
searchable, multi-select, async loading, creatable options, grouped menus, and a
combobox that is keyboard- and screen-reader-correct out of the box.
Algorize UI ships its own Select for the ordinary
case. Reach for react-select when you need the search, the async loading or the
tag input — and then make it look like everything else.
Unstyled, then repainted
The whole integration is two props.
unstyled switches off every emotion style
react-select injects, leaving a fully accessible but visually blank combobox.
classNames then paints each internal slot with Tailwind classes, so the control
ends up matching Input exactly — with no CSS
overrides and no specificity fights.Single and multi, on design tokens
Type to filter, arrow keys to move, Enter to pick. The control, the menu, the option rows and the multi-value chips are all painted from the same tokens as the rest of the form.
- Use
unstyled - before anything else. Without it you are layering classes on top of emotion styles and losing to them.
classNames- over
styles. Both exist;classNameskeeps the styling in Tailwind where the rest of the app is, and each slot receives the live state (isFocused,isSelected,isDisabled). components- swaps a single internal part — here
Option, to add the hint line and the tick — while leaving the behaviour untouched. inputId- is what a
<label htmlFor>points at.idlands on the wrapper, not the input, so a label wired to it does nothing.
Installation
This example also uses these Algorize UI components — install whichever
your project does not already have:
Two things that will bite
A
boolean-typed classNames config widens onChange everywhere. react-select
infers IsMulti from the isMulti prop, and a config annotated
ClassNamesConfig<Option, boolean, ...> drags that boolean back into the
component's own inference — so onChange starts handing you "one option or
many" at every call site. Build the config from a small generic factory instead
and instantiate it once per mode, as the snippet does.unstyled is not quite total. A few functional emotion rules survive, and one
of them sets display: block on the option element. Laying a row out with
flex in the option classNames therefore does nothing. Put the layout one
level in, inside your own wrapper element, rather than reaching for !flex.Going further
Everything below is the same
classNames config with a different entry point:| Need | Import |
|---|---|
| Options loaded from an API as you type | AsyncSelect from react-select/async |
| Let people add options that do not exist | CreatableSelect from react-select/creatable |
| Both | AsyncCreatableSelect from react-select/async-creatable |
Each takes the same
unstyled, classNames and components props, so the
styling above carries over unchanged — only the data source differs.For a plain dropdown with no search and no tags, the built-in
Select is smaller and needs no configuration.