Vanilla JS Custom Select for Bootstrap 5 – bootstrap-select

Category: Form , Javascript | August 16, 2026
Authorgianlucagiacometti
Last UpdateAugust 16, 2026
LicenseMIT
Views12 views
Vanilla JS Custom Select for Bootstrap 5 – bootstrap-select

bootstrap-select is a vanilla JavaScript library that enhances Bootstrap’s <select> component with search, checkbox-based multiple selection, rich option content, and programmatic value management.

It builds the interactive control with Bootstrap Input and Dropdown components while keeping the original <select> synchronized with the selected values.

Features:

  • Single and multiple selection modes.
  • Type-to-filter search for long option lists.
  • Case-insensitive and diacritic-insensitive matching.
  • Checkbox selection for multi-select fields.
  • Select-all control for multiple selections.
  • Option groups with grouped selection support.
  • Icons, images, comments, and visual dividers inside option lists.
  • Programmatic sorting, value updates, insertion, and removal.
  • Disabled and readonly states controlled at runtime.
  • Automatic initialization through a CSS class.
  • Native change events on the underlying select element.

How To Use It

Installation

Load Bootstrap framework first, followed by the component stylesheet and JavaScript.Bootstrap Icons are needed when you use the built-in search clear icon or Bootstrap icon names inside options.

<!-- Bootstrap -->
<link rel="stylesheet" href="/assets/bootstrap/bootstrap.min.css">
<!-- Optional for search and Bootstrap option icons -->
<link rel="stylesheet" href="/assets/bootstrap-icons/bootstrap-icons.css">
<!-- bootstrap-select -->
<link rel="stylesheet" href="/assets/bootstrap-select/bootstrap-select.css">
<script src="/assets/bootstrap/bootstrap.bundle.min.js"></script>
<script src="/assets/bootstrap-select/bootstrap-select.js"></script>

Basic Usage

Add the bootstrap-select class to a regular select field. The component initializes it automatically.

<label for="project-role" class="form-label">Project role</label>
<select id="project-role" class="bootstrap-select">
  <option value="">Choose a role</option>
  <option value="designer">Designer</option>
  <option value="developer">Developer</option>
  <option value="manager">Project Manager</option>
</select>

Listen for its normal change event when your app logic depends on the selected value.

const roleSelect = document.querySelector('#project-role');
roleSelect.addEventListener('change', function () {
  console.log(roleSelect.value);
});

Add Search

Add the searchable class to place a search field above the option list. data-bs-search-text changes its placeholder.

<label for="timezone-select" class="form-label">Time zone</label>
<select
  id="timezone-select"
  class="bootstrap-select searchable"
  data-bs-search-text="Filter time zones"
>
  <option value="new-york">New York</option>
  <option value="sao-paulo">São Paulo</option>
  <option value="paris">Paris</option>
  <option value="tokyo">Tokyo</option>
</select>

Multiple Selection

Use the standard multiple property for checkbox-based multi-selection. Add data-bs-toggle-button="true" when the list also needs a checkbox that selects or clears all available options.

<label for="region-select" class="form-label">Sales regions</label>
<select
  id="region-select"
  class="bootstrap-select searchable"
  multiple
  data-bs-toggle-button="true"
  data-bs-search-text="Find a region"
>
  <option value="north-america">North America</option>
  <option value="south-america">South America</option>
  <option value="europe">Europe</option>
  <option value="asia-pacific">Asia Pacific</option>
</select>

Read selected values through selectedOptions when you only need native DOM access.

const regionSelect = document.querySelector('#region-select');
regionSelect.addEventListener('change', function () {
  const regions = [...regionSelect.selectedOptions].map(option => option.value);
  console.log(regions);
});

Group Multiple Choices

Standard <optgroup> elements organize related choices. Multi-select mode also adds a checkbox to each group.

<label for="stack-select" class="form-label">Technology stack</label>
<select id="stack-select" class="bootstrap-select searchable" multiple>
  <optgroup label="Frontend">
    <option value="react">React</option>
    <option value="vue">Vue</option>
    <option value="svelte">Svelte</option>
  </optgroup>
  <optgroup label="Backend">
    <option value="node">Node.js</option>
    <option value="php">PHP</option>
    <option value="python">Python</option>
  </optgroup>
</select>

Add Icons, Images, And Comments

Option data attributes add richer content to the generated dropdown.

<label for="deployment-select" class="form-label">Deployment target</label>
<select id="deployment-select" class="bootstrap-select">
  <option
    value="cloud"
    data-bs-select-option-icon="cloud"
    data-bs-select-option-comment="Managed cloud environment"
  >
    Cloud
  </option>
  <option
    value="edge"
    data-bs-select-option-icon="hdd-network"
    data-bs-select-option-comment="Distributed edge deployment"
  >
    Edge
  </option>
  <option
    value="local"
    data-bs-select-option-icon="pc-display"
    data-bs-select-option-comment="Runs on local infrastructure"
  >
    Local
  </option>
</select>

An image can sit on the right side of an option.

<option
  value="canada"
  data-bs-select-option-image="/images/flags/canada.svg"
  data-bs-select-option-image-class="rounded-1"
>
  Canada
</option>

A custom icon library can replace Bootstrap Icons through data-bs-select-option-icon-class.

<option
  value="germany"
  data-bs-select-option-icon-class="fi fi-de"
>
  Germany
</option>

Add Dividers

An option with data-bs-select-option-divider="true" generates as a visual divider.

Dividers work best inside an <optgroup>, where their position stays associated with the surrounding group.

<optgroup label="Account actions">
  <option value="profile">Edit profile</option>
  <option value="security">Security settings</option>
  <option data-bs-select-option-divider="true"></option>
  <option value="signout">Sign out</option>
</optgroup>

Select Classes

  • bootstrap-select: Initializes the select automatically.
  • searchable: Adds the filter input.
  • form-select-lg: Applies Bootstrap’s large input sizing.
  • form-select-sm: Applies Bootstrap’s small input sizing.
  • form-select-plaintext: Applies Bootstrap’s plaintext form style.
  • label-floating: Uses a Bootstrap floating-label layout.
  • label-outline: Applies the component’s outlined label style.

Select Attributes And Properties

  • multiple (boolean): Enables multiple selection.
  • disabled (boolean): Starts the entire control in a disabled state.
  • data-bs-search-text (string): Sets the search field placeholder. The default is Search....
  • data-bs-toggle-button (boolean): Adds the select-all checkbox to a multiple select when set to true.

Option Attributes

  • data-bs-select-option-icon (string): Adds a Bootstrap Icon before the option text. Supply the icon name without the bi- prefix.
  • data-bs-select-option-icon-class (string): Adds classes to the option icon element. It also supports another icon library when no Bootstrap icon name is set.
  • data-bs-select-option-image (string): Sets the image URL shown at the right side of an option.
  • data-bs-select-option-image-class (string): Adds custom classes to the generated option image.
  • data-bs-select-option-comment (string or HTML): Adds supporting content below the main option text.
  • data-bs-select-option-divider (string): Renders the option as a divider when set to true.

Manual Initialization

The constructor accepts an optional visibility parameter.

  • visibility (string, default collapsed): Accepts collapsed or expanded.
<label for="status-select" class="form-label">Status</label>
<select id="status-select">
  <option value="draft">Draft</option>
  <option value="review">In review</option>
  <option value="published">Published</option>
</select>
const sequence =
  `${Date.now()}${Math.floor(Math.random() * 1000)}`;
const statusSelect = new bsSelect(
  'status-select',
  sequence,
  {
    visibility: 'expanded'
  }
);

Work With Automatically Created Instances

Automatically initialized controls are stored by select ID in FORM.select.

const projectSelect = FORM.select['project-role'];

This instance exposes methods for sorting, selection state, removal, insertion, disabled state, readonly state, and cleanup.

Sort Options

A string is enough for a basic ascending or descending sort.

const languageSelect = FORM.select['language-select'];
// Sort alphabetically.
languageSelect.sort('asc');
// Reverse alphabetical order.
languageSelect.sort('desc');

More detailed sorting behavior uses an object.

languageSelect.sort({
  mode: 'asc',
  disabled: 'bottom',
  sortDisabled: 'true',
  emptyDisabledValues: 'top',
  optionGroups: 'include'
});

The sorting parameters are:

  • mode (string, default asc): Accepts asc or desc.
  • disabled (string, default ignore): Accepts ignore, top, bottom, or include.
  • sortDisabled (string, default true): Controls whether disabled choices are sorted when disabled options receive special placement.
  • emptyDisabledValues (string, default top): Accepts top or include.
  • optionGroups (string, default include): Accepts include or ignore.

Dividers inside an option group preserve that group’s order instead of participating in recursive sorting.

Get And Set Values

Calling value() with no argument returns the selected value. Multiple selects return an array.

const regionPicker = FORM.select['region-select'];
// Read all selected values.
const selectedRegions = regionPicker.value();
console.log(selectedRegions);

Set one or several values through the same method.

// Replace the existing selection.
regionPicker.value(
  ['north-america', 'europe'],
  {
    swap: true,
    disabled: false
  }
);

Set swap to false when new values should join the existing selection.

regionPicker.value(
  ['asia-pacific'],
  {
    swap: false,
    disabled: false
  }
);

Disabled options are skipped by default. Pass disabled: true only when application logic deliberately needs to select one.

Remove Options

Remove options by value, ID, or the component’s internal option index.

const categorySelect = FORM.select['category-select'];
// Remove one value.
categorySelect.remove({
  value: 'archived'
});
// Remove several values.
categorySelect.remove({
  values: ['draft', 'legacy']
});
// Remove an optgroup or option by ID.
categorySelect.remove({
  id: 'inactive-categories'
});
// String shorthand for an ID.
categorySelect.remove('inactive-categories');

API Methods

const selectInstance = FORM.select['project-role'];
// Sort options.
// Accepts "asc", "desc", or a sorting configuration object.
selectInstance.sort('asc');
// Insert option or optgroup elements.
// The optional second argument targets an optgroup.
selectInstance.insert(
  '<option value="qa">QA Engineer</option>'
);
// Remove options or groups by value, ID, or internal index.
selectInstance.remove({
  value: 'qa'
});
// Get the current value.
const currentValue = selectInstance.value();
// Set the current value.
selectInstance.value('developer');
// Enable or disable the control.
selectInstance.disabled(true);
selectInstance.disabled(false);
// Apply or remove readonly behavior.
selectInstance.readonly(true);
selectInstance.readonly(false);
// Remove the enhanced UI and restore the native select and label.
selectInstance.destroy();

Change Events

Selection changes and state-changing API operations dispatch the standard bubbling change event on the original select.

const departmentSelect =
  document.querySelector('#department-select');
departmentSelect.addEventListener('change', function (event) {
  console.log(event.target.value);
});

For a multiple select, read all selected options from the original element.

departmentSelect.addEventListener('change', function () {
  const values = [...departmentSelect.selectedOptions]
    .map(option => option.value);
  console.log(values);
});

Alternatives:

Changelog:

v5.3.10 (08/16/2026)

  • Improved multiple-select toggle-all control

You Might Be Interested In:


Leave a Reply