react-select, react-dropdown-select, and react-selectize are libraries designed to replace the native HTML <select> element with customizable React components. react-select is the industry standard, offering extensive accessibility features and customization options for complex forms. react-dropdown-select provides a lighter-weight alternative with a simpler API for standard use cases. react-selectize is a legacy package that mimics the Selectize.js jQuery plugin but lacks modern React support and maintenance.
Replacing the native HTML <select> element is a common requirement in modern web applications. Designers often need custom styling, while developers need accessibility, keyboard navigation, and complex data handling. react-select, react-dropdown-select, and react-selectize attempt to solve this, but they differ significantly in maintenance, API design, and feature depth. Let's compare how they handle real-world engineering challenges.
Before writing code, you must consider the long-term health of the dependency.
react-select is actively maintained by a dedicated team and community. It receives regular updates for React compatibility and security patches. It is the safest choice for production systems.
react-dropdown-select is maintained but with a smaller scope. It receives updates occasionally and is stable for standard use cases. It is a viable alternative if you need less complexity.
react-selectize is effectively abandoned. It has not seen significant updates in years and does not align with modern React patterns. Using it introduces technical debt immediately.
Getting a simple dropdown running varies slightly in API design across these libraries.
react-select uses a straightforward options array and onChange handler. It handles state internally unless controlled.
// react-select
import Select from 'react-select';
const options = [
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'strawberry', label: 'Strawberry' }
];
function FlavourSelect() {
return <Select options={options} />;
}
react-dropdown-select looks similar but expects values as an array for controlled components and uses onChange returning the selected item.
// react-dropdown-select
import Select from 'react-dropdown-select';
const options = [
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'strawberry', label: 'Strawberry' }
];
function FlavourSelect() {
return <Select options={options} placeholder="Select" />;
}
react-selectize uses a different prop naming convention, often requiring value and onValueChange. It feels older and less consistent with modern React standards.
// react-selectize
import { Selectize } from 'react-selectize';
const options = [
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'strawberry', label: 'Strawberry' }
];
function FlavourSelect() {
return <Selectize options={options} />;
}
Handling multiple selections is where these libraries diverge in complexity and ease of use.
react-select enables multi-select with a simple boolean prop. It manages tags, removal, and keyboard navigation automatically.
// react-select
<Select
options={options}
isMulti={true}
placeholder="Select flavours"
/>
react-dropdown-select also supports multi-select via a prop. It renders selected items as removable chips within the input.
// react-dropdown-select
<Select
options={options}
multi={true}
placeholder="Select flavours"
/>
react-selectize supports multi-select but requires more manual configuration for styling and behavior. The API feels less polished compared to modern alternatives.
// react-selectize
<Selectize
options={options}
multi={true}
placeholder="Select flavours"
/>
Customizing the look and feel is often the main reason to avoid native selects. Each library offers a different approach.
react-select uses a components prop to override internal parts like Option, Control, or Menu. This allows deep structural changes without hacking CSS.
// react-select
const CustomOption = (props) => (
<components.Option {...props}>
<strong>{props.label}</strong>
</components.Option>
);
<Select options={options} components={{ Option: CustomOption }} />
react-dropdown-select relies on render props for content customization and className for styling hooks. It is simpler but less flexible for structural changes.
// react-dropdown-select
<Select
options={options}
renderItem={(item)} => <span>{item.label}</span>
className="custom-select"
/>
react-selectize depends heavily on CSS classes and legacy render props. Customizing structure often requires overriding default styles globally, which can cause conflicts.
// react-selectize
<Selectize
options={options}
renderOption={(option)} => <div>{option.label}</div>
className="selectize-input"
/>
Loading options from an API is a common requirement for large datasets.
react-select provides a dedicated Async component. It handles debouncing, loading states, and caching out of the box.
// react-select
import AsyncSelect from 'react-select/async';
const loadOptions = (inputValue) => {
return fetch(`/api/search?q=${inputValue}`).then(res => res.json());
};
<AsyncSelect loadOptions={loadOptions} />
react-dropdown-select does not have a built-in async component. You must manage loading state and option fetching manually in the parent component.
// react-dropdown-select
const [options, setOptions] = useState([]);
useEffect(() => {
fetch('/api/options').then(res => res.json()).then(setOptions);
}, []);
<Select options={options} />
react-selectize lacks built-in async support. Developers must implement their own debouncing and loading logic, increasing the risk of bugs.
// react-selectize
const [options, setOptions] = useState([]);
useEffect(() => {
fetch('/api/options').then(res => res.json()).then(setOptions);
}, []);
<Selectize options={options} />
Accessibility is critical for public-facing applications. Screen readers must understand the input.
react-select has robust ARIA support built-in. It manages focus, live regions, and keyboard navigation according to WAI-ARIA standards.
// react-select
<Select
options={options}
aria-label="Flavour selection"
instanceId="unique-id"
/>
react-dropdown-select includes basic ARIA attributes but may require manual testing to ensure full compliance with screen readers.
// react-dropdown-select
<Select
options={options}
aria-label="Flavour selection"
/>
react-selectize has outdated accessibility implementations. It often fails modern audit tools and lacks proper keyboard navigation support.
// react-selectize
<Selectize
options={options}
aria-label="Flavour selection"
/>
| Feature | react-select | react-dropdown-select | react-selectize |
|---|---|---|---|
| Maintenance | ✅ Active | ✅ Active | ❌ Abandoned |
| Bundle Size | 📦 Larger | 📦 Smaller | 📦 Small |
| Async Support | ✅ Built-in Component | ❌ Manual Implementation | ❌ Manual Implementation |
| Customization | 🎨 Deep (Components) | 🎨 Medium (Render Props) | 🎨 Low (CSS/Props) |
| Accessibility | ✅ High (WAI-ARIA) | ⚠️ Moderate | ❌ Low/Outdated |
| Multi-Select | ✅ Native Prop | ✅ Native Prop | ✅ Native Prop |
react-select is the clear winner for professional development. It handles the hard problems — accessibility, async loading, and complex state — so you don't have to. The learning curve is slightly steeper, but the long-term stability is worth it.
react-dropdown-select is a solid backup if you need something lighter and simpler. It works well for internal dashboards or projects where react-select feels like overkill.
react-selectize should be avoided. It belongs to an older era of React development. Using it today creates unnecessary risk and technical debt. If you find it in an existing codebase, plan to replace it during your next refactor.
Choose react-dropdown-select if you need a simpler implementation with less boilerplate than react-select. It works well for standard dropdowns where you want control over styling without managing a heavy component API. Ideal for internal tools or projects where bundle weight is a higher priority than advanced features.
Choose react-select for enterprise applications requiring strict accessibility compliance, complex async loading, or deep customization. It is the safest long-term choice due to active maintenance and a large community. Use this when your project needs multi-select, grouping, or creatable options out of the box.
Do NOT choose react-selectize for new projects. It is unmaintained and lacks support for modern React versions and accessibility standards. Existing projects using this library should plan a migration to react-select or react-dropdown-select to avoid security risks and compatibility issues.
Customisable dropdown select for react
propsdocument.body
npm install --save react-dropdown-select
react-select is very nice, but sometimes project requirements are beyond it's abilities
import:
import Select from "react-dropdown-select";
and use as:
const options = [
{
value: 1,
label: 'Leanne Graham'
},
{
value: 2,
label: 'Ervin Howell'
}
];
<Select options={options} onChange={(values) => this.setValues(values)} />;
If your options don't have value and label fields, include labelField and valueField in the props:
const options = [
{
id: 1,
name: 'Leanne Graham'
},
{
id: 2,
name: 'Ervin Howell'
}
];
<Select
options={options}
labelField="name"
valueField="id"
onChange={(values) => this.setValues(values)}
/>;
options and onChange are the minimum required props
| Prop | Type | Default | Description |
|---|---|---|---|
| values | array | [] | Selected values |
| options | array | [] | Available options, (option with key disabled: true will be disabled) |
| keepOpen | bool | false | If true, dropdown will always stay open (good for debugging) |
| defaultMenuIsOpen | bool | false | If true, dropdown will be open by default |
| autoFocus | bool | false | If true, and searchable, dropdown will auto focus |
| clearOnBlur | bool | true | If true, and searchable, search value will be cleared on blur |
| clearOnSelect | bool | true | If true, and searchable, search value will be cleared upon value select/de-select |
| name | string | null | If set, input type hidden would be added in the component with the value of the name prop as name and select's values as value |
| required | bool | false | If set, input type hidden would be added in the component with required prop as true/false |
| pattern | string | null | If set, input type hidden would be added in the component with pattern prop as regex |
| dropdownGap | number | 5 | Gap between select element and dropdown |
| multi | bool | false | If true - will act as multi-select, if false - only one option will be selected at the time |
| placeholder | string | "Select..." | Placeholder shown where there are no selected values |
| addPlaceholder | string | "" | Secondary placeholder on search field if any value selected |
| disabled | bool | false | Disable select and all interactions |
| style | object | {} | Style object to pass to select |
| className | string | CSS class attribute to pass to select | |
| loading | bool | false | Loading indicator |
| clearable | bool | false | Clear all indicator |
| searchable | bool | true | If true, select will have search input text |
| separator | bool | false | Separator line between close all and dropdown handle |
| dropdownHandle | bool | true | Dropdown handle to open/close dropdown |
| dropdownHeight | string | "300px" | Minimum height of a dropdown |
| direction | string | "ltr" | direction of a dropdown "ltr", "rtl" or "auto" |
| searchBy | string | label | Search by object property in values |
| sortBy | string | null | Sort by object property in values |
| labelField | string | "label" | Field in data to use for label |
| valueField | string | "value" | Field in data to use for value |
| color | string | "#0074D9" | Base color to use in component, also can be overwritten via CSS |
| closeOnScroll | bool | false | If true, scrolling the page will close the dropdown |
| closeOnSelect | bool | false | If true, selecting option will close the dropdown |
| closeOnClickInput | bool | false | If true, clicking input will close the dropdown if you are not searching. |
| dropdownPosition | string | "bottom" | Available options are "auto", "top" and "bottom" defaults to "bottom". Auto will adjust itself according Select's position on the page |
| keepSelectedInList | bool | true | If false, selected item will not appear in a list |
| portal | DOM element | false | If valid dom element specified - dropdown will break out to render inside the specified element |
| create | bool | false | If true, select will create value from search string and fire onCreateNew callback prop |
| backspaceDelete | bool | true | If true, backspace key will delete last value |
| createNewLabel | string | "add {search}" | If create set to true, this will be the label of the "add new" component. {search} will be replaced by search value |
| disabledLabel | string | "disabled" | Label shown on disabled field (after) the text |
| selectAll | bool | false | Allow to select all |
| selectAllLabel | string | "Select all" | Label for "Select all" |
| clearAllLabel | string | "Clear all" | Label for "Clear all" |
| additionalProps | object | null | Additional props to pass to Select |
by using renderer props to override components some of the functionality will have to be handled manually with a help of internal props, states and methods exposed
| Prop | Type | Default | Description |
|---|---|---|---|
| onChange | func | On values change (user and internally triggered) callback, returns array of values objects | |
| onSelect | func | On values change (user triggered) callback, returns array of values objects | |
| onDeselect | func | On values change (user triggered) callback, returns array of values objects | |
| onDropdownClose | func | Fires upon dropdown close | |
| onDropdownOpen | func | Fires upon dropdown open | |
| onCreateNew | func | Fires upon creation of new item if create prop set to true | |
| onClearAll | func | Fires upon clearing all values (via custom renderers) | |
| onSelectAll | func | Fires upon selecting all values (via custom renderers) | |
| onDropdownCloseRequest | func | undefined | Fires upon dropdown closing state, stops the closing and provides own method close() |
| contentRenderer | func | Overrides internal content component (the contents of the select component) | |
| itemRenderer | func | Overrides internal item in a dropdown | |
| noDataRenderer | func | Overrides internal "no data" (shown where search has no results) | |
| optionRenderer | func | Overrides internal option (the pillow with an "x") on the select content | |
| inputRenderer | func | Overrides internal input text | |
| loadingRenderer | func | Overrides internal loading | |
| clearRenderer | func | Overrides internal clear button | |
| separatorRenderer | func | Overrides internal separator | |
| dropdownRenderer | func | Overrides internal dropdown component | |
| dropdownHandleRenderer | func | Overrides internal dropdown handle | |
| searchFn | func | undefined | Overrides internal search function |
| handleKeyDownFn | func | undefined | Overrides internal keyDown function |