DEV Community

Alexandro Paixao Marques
Alexandro Paixao Marques

Posted on

React Multiselect Dropdown 19: controlled state, skins, modals, and accessibility

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
Enter fullscreen mode Exit fullscreen mode

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:

  • data receives the list of options;
  • selectedItems stores the selected items;
  • onChange returns the updated selection;
  • settings controls 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}
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

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'
};
Enter fullscreen mode Exit fullscreen mode

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'
};
Enter fullscreen mode Exit fullscreen mode

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'
};
Enter fullscreen mode Exit fullscreen mode

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'
};
Enter fullscreen mode Exit fullscreen mode

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'
};
Enter fullscreen mode Exit fullscreen mode

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'
};
Enter fullscreen mode Exit fullscreen mode

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>
  )}
/>
Enter fullscreen mode Exit fullscreen mode

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()));
  }}
/>
Enter fullscreen mode Exit fullscreen mode

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
};
Enter fullscreen mode Exit fullscreen mode

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')}
/>
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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)