Multi-select Dropdown Component For JavaScript – slim-select

Category: Form , Javascript , Recommended | January 6, 2026
Authorbrianvoe
Last UpdateJanuary 6, 2026
LicenseMIT
Views47,678 views
Multi-select Dropdown Component For JavaScript – slim-select

slim-select is a lightweight and dependency-free JavaScript library that creates searchable, accessible, multi-selectable dropdowns from regular HTML select elements.

Features:

  • Multi-Select: You can select multiple options with customizable selection limits.
  • Real-Time Search: Built-in search input filters options as you type with customizable search functions.
  • WCAG 2.1 Level AA Compliant: Implements proper ARIA attributes and keyboard navigation for screen reader users.
  • Framework Compatible: Official Vue 3 and React components with full reactivity and TypeScript support.
  • Custom HTML Options: Render rich HTML content inside option labels beyond plain text.
  • Dynamic Data Management: Programmatically add, remove, or update options through API methods.
  • Event Callbacks: Hook into selection changes, dropdown open/close, and search events.
  • Addable Options: Allow you to create new options on the fly with validation.
  • Performance Optimized: Uses efficient DOM updates and requestAnimationFrame for smooth animations.
  • Placeholder Support: Display instructional text when no value is selected.
  • Disabled States: Control which options users can select at runtime.
  • CSS Customization: Override default styles through CSS custom properties or class inheritance.
  • TypeScript Definitions: Full type safety with included TypeScript declaration files.

Use Cases:

  • Advanced Form Interfaces: Build complex multi-step forms that need searchable dropdowns with validation rules.
  • E-commerce Product Filters: Create filterable product attribute selectors (size, color, brand) with multi-select support.
  • Admin Dashboards: Implement user role selectors, status filters, and tag pickers with custom option rendering.
  • Data Import Tools: Allow users to map CSV columns to database fields using dropdown selectors with search.
  • Survey and Quiz Platforms: Build accessible form controls that work with screen readers for WCAG compliance.
  • Configuration Panels: Provide settings interfaces where users need to select multiple options from large lists.

How To Use It:

1. Import slim-select with NPM and import it into your project.

# Yarn
$ yarn add slim-select
# NPM
$ npm install slim-select
// Import the SlimSelect class
import SlimSelect from 'slim-select'
// Import styles (choose one method)
import 'slim-select/styles' // CSS import
// OR
import 'slim-select/scss' // SCSS import for customization

2. Or download the package and load the following files in your HTML document.

<!-- Local -->
<link rel="stylesheet" href="/dist/slimselect.css" />
<script src="/dist/slimselect.js"></script>
<!-- OR from a CDN -->
<link rel="stylesheet" href="https://unpkg.com/slim-select@latest/dist/slimselect.css" />
<script src="https://unpkg.com/slim-select@latest/dist/slimselect.js"></script>

3. Create a standard HTML select element and initialize SlimSelect:

<select id="demo">
  <option value="value 1">JavaScript</option>
  <option value="value 2">CSS3</option>
  <option value="value 3">HTML5</option>
</select>
new SlimSelect({
    select: '#demo'
})

4. The library also supports option groups:

<select id="single-optgroups">
  <optgroup label="JavaScript">
    <option value="value 1">Angular</option>
    <option value="value 2">React</option>
    <option value="value 3">Vue</option>
  </optgroup>
  <optgroup label="CSS">
    <option value="value 4">Bootstrap</option>
    <option value="value 5">Foundation</option>
    <option value="value 6">Bulma</option>
  </optgroup>
</select>

5. And multi-select:

<select id="single-optgroups" multiple>
  <optgroup label="JavaScript">
    <option value="value 1">Angular</option>
    <option value="value 2">React</option>
    <option value="value 3">Vue</option>
  </optgroup>
  <optgroup label="CSS">
    <option value="value 4">Bootstrap</option>
    <option value="value 5">Foundation</option>
    <option value="value 6">Bulma</option>
  </optgroup>
</select>

6. SlimSelect accepts data as an array of option and optgroup objects. You can initialize the select with data instead of relying on HTML markup:

Optgroup Object:

  • label (string, required): The visible text for the option group header.
  • selectAll (boolean, optional, default: false): Adds a “Select All” option to the group in multi-select mode.
  • closable (string, optional, default: ‘off’): Controls group collapse behavior. Values: ‘off’, ‘open’, ‘close’.
  • options (array, required): Array of option objects belonging to this group.

Option Object:

  • text (string, required): The display text for the option.
  • value (string, optional): The underlying value. Defaults to text if not provided.
  • html (string, optional): Custom HTML to render instead of plain text.
  • selected (boolean, optional, default: false): Marks the option as initially selected.
  • display (boolean, optional, default: true): Controls visibility of the option in the dropdown.
  • disabled (boolean, optional, default: false): Prevents selection of the option.
  • mandatory (boolean, optional, default: false): Prevents deselection in multi-select mode.
  • placeholder (boolean, optional, default: false): Marks the option as a placeholder that disappears after selection.
  • class (string, optional): CSS class name to apply to the option element.
  • style (string, optional): Inline CSS styles to apply to the option element.
  • data (object, optional): Custom data attributes to attach to the option element.
new SlimSelect({
  select: '#demo',
  
  // Simple array of options
  data: [
    { text: 'Apple', value: 'apple' },
    { text: 'Banana', value: 'banana' },
    { text: 'Cherry', value: 'cherry' }
  ]
  
  // OR grouped options with optgroups
  // data: [
  //   {
  //     label: 'Fruits',
  //     options: [
  //       { text: 'Apple', value: 'apple' },
  //       { text: 'Banana', value: 'banana' }
  //     ]
  //   },
  //   {
  //     label: 'Vegetables',
  //     options: [
  //       { text: 'Carrot', value: 'carrot' }
  //     ]
  //   }
  // ]
})

6. All possible configuration options:

  • disabled (boolean, default: false): Disables the entire select component. Users cannot interact with it.
  • alwaysOpen (boolean, default: false): Keeps the dropdown permanently open. Useful for inline selection interfaces.
  • showSearch (boolean, default: true): Displays the search input field at the top of the dropdown.
  • focusSearch (boolean, default: true): Automatically focuses the search input when the dropdown opens.
  • keepSearch (boolean, default: false): Retains the search query text when the dropdown closes and reopens.
  • ariaLabel (string, default: ‘Combobox’): Sets the ARIA label for screen readers.
  • searchPlaceholder (string, default: ‘Search’): Placeholder text displayed in the search input.
  • searchText (string, default: ‘No Results’): Message shown when search returns no matching options.
  • searchingText (string, default: ‘Searching…’): Loading message displayed during asynchronous search operations.
  • searchHighlight (boolean, default: false): Highlights matching text within options during search.
  • closeOnSelect (boolean, default: true): Automatically closes the dropdown after an option is selected. Set to false for multi-select workflows.
  • contentLocation (DOM element, default: document.body): Specifies where to append the dropdown element in the DOM tree.
  • contentPosition (string, default: ‘absolute’): CSS position property for the dropdown. Values: ‘absolute’, ‘relative’, ‘fixed’.
  • openPosition (string, default: ‘auto’): Controls dropdown opening direction. Values: ‘auto’, ‘up’, ‘down’.
  • placeholderText (string, default: ‘Select Value’): Text shown when no option is selected.
  • allowDeselect (boolean, default: false): Allows users to deselect the current option in single-select mode by clicking it again.
  • hideSelected (boolean, default: false): Removes selected options from the dropdown list in multi-select mode.
  • keepOrder (boolean, default: false): Maintains user selection order instead of DOM order for getSelected() method.
  • showOptionTooltips (boolean, default: false): Displays browser tooltips on hover for options with title attributes.
  • minSelected (number, default: 0): Minimum number of options that must be selected in multi-select mode.
  • maxSelected (number, default: 1000): Maximum number of options that can be selected in multi-select mode.
  • timeoutDelay (number, default: 200): Debounce delay in milliseconds for event callbacks.
  • maxValuesShown (number, default: 20): Number of selected values to display before showing a summary message.
  • maxValuesMessage (string, default: ‘{number} selected’): Template for the summary message. Use {number} placeholder for the count.
  • addableText (string, default: ‘Press “Enter” to add {value}’): Instruction text for the addable option feature. Use {value} placeholder for user input.
new SlimSelect({
  select: '#demo',
  settings: {
    disabled: false,
    alwaysOpen: false,
    showSearch: true,
    focusSearch: true, 
    keepSearch: false,
    ariaLabel: 'Combobox',
    searchPlaceholder: 'Search',
    searchText: 'No Results',
    searchingText: 'Searching...', 
    searchHighlight: false, 
    closeOnSelect: true,
    contentLocation: document.body, 
    contentPosition: 'absolute', 
    openPosition: 'auto', 
    placeholderText: 'Select Value',
    allowDeselect: false, 
    hideSelected: false, 
    keepOrder: false, 
    showOptionTooltips: false,
    minSelected: 0,
    maxSelected: 1000,
    timeoutDelay: 200,
    maxValuesShown: 20, 
    maxValuesMessage: '{number} selected', 
    addableText: 'Press "Enter" to add {value}'
  }
})

7. Callback functions:

new SlimSelect({
  select: '#demo',
  
  events: {
    // Custom search function
    // Receives the current search value and existing data
    // Returns a Promise or array of filtered/fetched options
    search: (searchValue, currentData) => {
      // Example: Async API call
      return fetch(`/api/search?q=${searchValue}`)
        .then(response => response.json())
        .then(data => data.results);
      
      // OR: Synchronous filtering
      // return currentData.filter(item => 
      //   item.text.toLowerCase().includes(searchValue.toLowerCase())
      // );
    },
    
    // Custom search filter function
    // Returns true to show the option, false to hide it
    searchFilter: (option, search) => {
      return option.text.toLowerCase().includes(search.toLowerCase());
    },
    
    // Allow users to add new options
    // Returns the new option object or an error message
    addable: (value) => {
      // Validate the input
      if (value.length < 3) {
        return 'Value must be at least 3 characters';
      }
      
      // Return new option object
      return { text: value, value: value.toLowerCase().replace(/\s/g, '-') };
      
      // OR: Return a Promise for async validation
      // return fetch('/api/validate', { method: 'POST', body: JSON.stringify({ value }) })
      //   .then(response => response.json());
    },
    
    // Fires before selection changes
    // Return false to prevent the change
    beforeChange: (newVal, oldVal) => {
      console.log('About to change from:', oldVal, 'to:', newVal);
      // Example: Prevent deselecting the last option
      if (newVal.length === 0) {
        return false;
      }
    },
    
    // Fires after selection changes
    afterChange: (newVal) => {
      console.log('Selection changed to:', newVal);
    },
    
    // Fires before dropdown opens
    beforeOpen: () => {
      console.log('Dropdown is about to open');
    },
    
    // Fires after dropdown opens
    afterOpen: () => {
      console.log('Dropdown is now open');
    },
    
    // Fires before dropdown closes
    beforeClose: () => {
      console.log('Dropdown is about to close');
    },
    
    // Fires after dropdown closes
    afterClose: () => {
      console.log('Dropdown is now closed');
    },
    
    // Global error handler
    error: (err) => {
      console.error('SlimSelect error:', err);
    }
  }
})

8. API methods:

// Store the instance to access methods
const slim = new SlimSelect({
  select: '#selectElement'
});
// Enable the select component
slim.enable();
// Disable the select component (users cannot interact)
slim.disable();
// Get the current data structure as an array
const currentData = slim.getData();
console.log(currentData); // Array of option/optgroup objects
// Replace all options with new data
slim.setData([
  { text: 'New Option 1', value: 'new1' },
  { text: 'New Option 2', value: 'new2' }
]);
// Get currently selected values as an array of strings
const selected = slim.getSelected();
console.log(selected); // ['value1', 'value2']
// Set selected values programmatically
slim.setSelected(['value1', 'value3']);
// Add a single option to the dropdown
slim.addOption({
  text: 'Dynamically Added',
  value: 'dynamic1',
  selected: false
});
// Programmatically open the dropdown
slim.open();
// Programmatically close the dropdown
slim.close();
// Trigger a search programmatically
slim.search('query text');
// Destroy the instance and restore original select element
slim.destroy();

Alternatives:

  • Choices.js: Similar feature set with a different API design. Choices.js uses a more verbose configuration object structure. It has better support for tag-style multi-select interfaces.
  • Select2: The most popular select library but requires jQuery as a dependency. Select2 has a mature ecosystem support, and plugin options.

FAQs

Q: How do I style the dropdown to match my design system?
A: SlimSelect inherits classes and styles from the original select element. You can also override CSS custom properties or target the .ss-main and .ss-content classes.

Q: Can I use SlimSelect with server-side rendered HTML?
A: Yes. Initialize SlimSelect after the DOM loads using DOMContentLoaded or your framework’s mount lifecycle. The library reads the existing select options and transforms them. No special server-side configuration is needed.

Q: Why is my dropdown not closing after selection in multi-select mode?
A: Multi-select mode keeps the dropdown open by default to allow multiple selections. Set closeOnSelect: false in single-select mode or closeOnSelect: true in multi-select mode to change this behavior.

Q: How do I handle validation errors from the addable option feature?
A: Return a string error message from the addable event callback. SlimSelect will display this message in the dropdown instead of adding the option. For async validation, return a Promise that rejects with an error message.

Q: Does SlimSelect work with forms that use the FormData API?
A: Yes. SlimSelect updates the hidden select element whenever values change. The FormData API reads from the original select element automatically. Form serialization works without additional configuration.

Changelog:

v3.0.0 (01/06/2025)

  • Major update
  • Updated doc
  • Updated demo

You Might Be Interested In:


3 thoughts on “Multi-select Dropdown Component For JavaScript – slim-select

    1. La Tortue Rose

      You can use mySelect.selected() and it returns an array with selected values

      Reply
  1. Nigel Goddard

    Can I set individual colors for the selected items, if so how?

    Reply

Leave a Reply