A React multiselect with testable examples, StackBlitz, and versioned compatibility
@stackline/react-multiselect-dropdown is a multiselect component for React applications, focused on controlled state, search, grouping, lazy loading, visual skins, custom rendering, dialog/modal support, and ADA-compliant keyboard and ARIA behavior.
The current React 19 package line is 19.0.2, with peer dependencies for React and React DOM >=19.0.0 <20.0.0. The official documentation also provides testable examples, a live demo, and a StackBlitz playground so developers can validate the component before installing it in a project.
Source: https://raw.githubusercontent.com/alexandroit/react-multiselect-dropdown/main/package.json
Installation
For React 19 projects:
npm install @stackline/react-multiselect-dropdown
The package includes the component styles and injects the CSS at runtime, so no additional CSS import is required for the default experience.
Source: https://raw.githubusercontent.com/alexandroit/react-multiselect-dropdown/main/README.md
Basic usage
The main API follows a simple React pattern:
-
datareceives the list of options; -
selectedItemsstores the selected items; -
onChangereturns the updated selection; -
settingscontrols visual and functional behavior.
import { useMemo, useState } from 'react';
import {
MultiSelectDropdown,
type DropdownSettings
} from '@stackline/react-multiselect-dropdown';
type Country = {
id: number;
itemName: string;
capital: string;
region: string;
};
const countries: Country[] = [
{ id: 1, itemName: 'Brazil', capital: 'Brasilia', region: 'South America' },
{ id: 2, itemName: 'Canada', capital: 'Ottawa', region: 'North America' },
{ id: 3, itemName: 'Portugal', capital: 'Lisbon', region: 'Europe' }
];
export function CountrySelector() {
const [selectedCountries, setSelectedCountries] = useState<Country[]>([]);
const settings = useMemo<DropdownSettings<Country>>(
() => ({
text: 'Select countries',
enableSearchFilter: true,
primaryKey: 'id',
labelKey: 'itemName',
badgeShowLimit: 3,
skin: 'classic'
}),
[]
);
return (
<MultiSelectDropdown
data={countries}
selectedItems={selectedCountries}
onChange={setSelectedCountries}
settings={settings}
/>
);
}
The selected values stay in the application state, while the component handles the interaction.
This model works well for forms, table filters, dashboards, admin screens, and any flow where the selected values need to be consumed by other parts of the interface.
Main features
The component covers the most common multiselect scenarios in React applications:
| Feature | Support |
|---|---|
| Multi-select | Yes |
| Single-select | Yes |
| Controlled state | Yes |
| Uncontrolled state | Yes |
| Search and filtering | Yes |
| Search by specific object properties | Yes |
| Grouping by field | Yes |
| Selection limit | Yes |
| Lazy loading | Yes |
| Add item from search | Yes |
| Custom item rendering | Yes |
| Custom badge rendering | Yes |
| Selection events | Yes |
| Ref methods | Yes |
| Visual skins | Yes |
| Modal/dialog support | Yes |
| Keyboard navigation and ARIA | Yes |
The documented features include search, grouping, custom render functions, lazy loading, ref methods, body overlay support for clipped layouts, and ADA-compliant keyboard/ARIA behavior.
Source: https://raw.githubusercontent.com/alexandroit/react-multiselect-dropdown/main/README.md
Settings as the main configuration point
Most of the behavior is controlled through the settings object.
const settings = {
text: 'Select items',
singleSelection: false,
enableSearchFilter: true,
searchPlaceholderText: 'Search',
primaryKey: 'id',
labelKey: 'itemName',
badgeShowLimit: 3,
maxHeight: 260,
showCheckbox: true,
skin: 'classic'
};
This keeps configuration explicit and easy to reuse.
In larger projects, shared presets can be created per use case:
export const filterDropdownSettings = {
text: 'Select filters',
enableSearchFilter: true,
selectAllText: 'Select all',
unSelectAllText: 'Clear all',
badgeShowLimit: 3,
maxHeight: 260,
skin: 'classic'
};
This helps keep filters, forms, and admin screens consistent without recreating the same behavior multiple times.
Skins
The component ships with ready-to-use skins:
| Skin | Usage |
|---|---|
classic |
Compact classic UI |
material |
Material-style controls and chips |
dark |
Dark interfaces |
custom |
CSS-variable base for customization |
brand |
Stackline visual skin |
The skin is changed directly through settings:
const settings = {
text: 'Select countries',
enableSearchFilter: true,
primaryKey: 'id',
labelKey: 'itemName',
skin: 'material'
};
This makes it possible to test visual variations without changing how the component receives data or returns selected values.
Search, groups, and selection limits
Search can be enabled with enableSearchFilter.
You can also define which object properties should be used by the search with searchBy.
const settings = {
text: 'Select countries',
enableSearchFilter: true,
searchBy: ['itemName', 'capital'],
primaryKey: 'id',
labelKey: 'itemName'
};
For grouped lists, provide the field used for grouping:
const settings = {
text: 'Select by region',
enableSearchFilter: true,
groupBy: 'region',
selectGroup: true,
primaryKey: 'id',
labelKey: 'itemName'
};
When a selection rule needs to limit how many items can be selected:
const settings = {
text: 'Select up to 2 items',
enableSearchFilter: true,
limitSelection: 2,
badgeShowLimit: 2,
primaryKey: 'id',
labelKey: 'itemName'
};
These features are useful for dashboard filters, permission selectors, category selection, user lists, segmentation flows, and forms with specific business rules.
Custom rendering
When an item needs to display more than plain text, the component allows custom rendering for both the option row and the selected badge.
<MultiSelectDropdown
data={countries}
selectedItems={selectedCountries}
onChange={setSelectedCountries}
settings={settings}
renderItem={(item, context) => (
<span>
<strong>{context.label}</strong>
<small>{item.capital}</small>
</span>
)}
renderBadge={(item) => (
<span>{item.itemName}</span>
)}
/>
This is useful for displaying metadata, status, descriptions, icons, avatars, or any visual composition using JSX.
Lazy loading and dynamic data
For larger lists, the component supports lazy loading.
const settings = {
text: 'Select people',
enableSearchFilter: true,
lazyLoading: true,
labelKey: 'name',
primaryKey: 'id',
maxHeight: 140
};
<MultiSelectDropdown
data={people}
selectedItems={selectedPeople}
onChange={setSelectedPeople}
settings={settings}
onScrollToEnd={() => {
setPeople((current) => current.concat(loadMorePeople()));
}}
/>
The onScrollToEnd callback makes it possible to load more data when the user reaches the end of the list.
This pattern can be used with local data, pagination, remote APIs, or incremental loading.
Usage inside modals, dialogs, and overflow layouts
A common dropdown issue appears when the component is rendered inside a modal, drawer, scrollable card, or container with overflow: hidden.
In these cases, the dropdown panel can be clipped by the parent container.
To handle this type of layout, the component supports appendToBody, tagToBody, and autoPosition.
const settings = {
text: 'Dialog dropdown',
enableSearchFilter: true,
skin: 'material',
appendToBody: true,
tagToBody: true,
autoPosition: true
};
With body overlay enabled, the opened panel is rendered against document.body, aligned to the original trigger, recalculated on scroll/resize events, and removed when the dropdown closes or the component unmounts.
Source: https://raw.githubusercontent.com/alexandroit/react-multiselect-dropdown/main/README.md
This matters for real application interfaces, especially modals, sidebars, grids, dashboards, and layouts with scrollable areas.
Accessibility
The package documents ADA-compliant support for keyboard navigation, focus states, and ARIA labels.
Source: https://raw.githubusercontent.com/alexandroit/react-multiselect-dropdown/main/package.json
In practice, this means the component is designed for flows where the user needs to:
- open and close the dropdown;
- navigate through options;
- search items;
- select and remove values;
- understand selected state;
- handle disabled state;
- continue through the form without losing focus.
For interactive components such as dropdowns and multiselects, accessibility is not just visual polish. It is part of the component contract.
Events and methods
The component exposes callbacks for the main interaction points:
<MultiSelectDropdown
data={countries}
selectedItems={selectedCountries}
onChange={setSelectedCountries}
settings={settings}
onSelect={(item) => console.log('selected', item)}
onDeSelect={(item) => console.log('removed', item)}
onSelectAll={(items) => console.log('selected all', items)}
onDeSelectAll={(items) => console.log('cleared all', items)}
onOpen={() => console.log('opened')}
onClose={() => console.log('closed')}
/>
It also provides ref methods for cases where imperative control makes sense:
dropdownRef.current?.openDropdown();
dropdownRef.current?.closeDropdown();
dropdownRef.current?.focusSearch();
dropdownRef.current?.selectAll();
dropdownRef.current?.deSelectAll();
dropdownRef.current?.clearSelection();
This helps with flows that use external buttons, keyboard shortcuts, modals, toolbars, or global screen actions.
StackBlitz and live examples
In addition to the documentation, the React 19 line includes a StackBlitz playground to test the component directly in the browser.
The examples cover scenarios such as basic usage, single selection, search filter, API search, grouping, templating, forms, virtual scrolling, lazy loading, dialogs, events, methods, disabled state, selection limit, styling, and body overlay.
Source: https://raw.githubusercontent.com/alexandroit/react-multiselect-dropdown/main/README.md
This makes it easier to validate behavior before installing the dependency in a project.
For UI components, live examples matter because many decisions only become visible in the browser: focus, scroll, width, badges, search, overlay positioning, modal behavior, and keyboard navigation.
React version compatibility
The package is designed to keep a compatible line for each React major version.
| Package family | React | Peer range | Tested window |
|---|---|---|---|
17.x |
React 17 | >=17.0.0 <18.0.0 |
17.0.0 -> 17.0.2 |
18.x |
React 18 | >=18.0.0 <19.0.0 |
18.0.0 -> 18.3.1 |
19.x |
React 19 | >=19.0.0 <20.0.0 |
19.0.2 -> 19.2.4 |
This separation makes it clear which package line should be used depending on the React version of the project. The documentation also states that the React 19 line was tested in a clean application with React 19.2.4 and @stackline/react-multiselect-dropdown@19.0.2.
Source: https://raw.githubusercontent.com/alexandroit/react-multiselect-dropdown/main/README.md
The goal is to keep the component updated, tested, and predictable as React versions evolve.
Conclusion
@stackline/react-multiselect-dropdown provides a React multiselect with the features that usually appear in real applications: controlled state, search, grouping, skins, lazy loading, custom rendering, events, ref methods, modal/overflow layout support, and keyboard/ARIA accessibility.
The React 19 line already includes documentation, a live demo, StackBlitz, and a test matrix.
The idea is to keep the component versioned by React major, with testable examples and consistent behavior for developers who need a dropdown in forms, filters, dashboards, and admin screens without relying only on static snippets.
Last source check: May 30, 2026.

Top comments (0)