Skip to main content Skip to docs navigation

Custom select

Enhance native <select> elements with Bootstrap-styled dropdown menus, custom HTML content, icons, descriptions, and live search.

Overview

The custom select component transforms standard <select> elements into fully styled Bootstrap dropdowns while maintaining form compatibility. Key features include:

  • Custom HTML content in options via data attributes
  • Icons and images alongside option text
  • Secondary description text below each option
  • Live search/filtering with keyword support
  • Checkmark indicator for selected items
  • Full keyboard navigation and accessibility
  • Automatic sync with the original <select> for form submission

Example

Add data-bs-toggle="custom-select" to any <select> element to enhance it. The original select is hidden but remains in the DOM for form submissions.

HTML
<label for="basicSelect" class="form-label">Choose an option</label>
<select class="form-control" id="basicSelect" data-bs-toggle="custom-select">
  <option value="">Select an item…</option>
  <option value="1">Option one</option>
  <option value="2">Option two</option>
  <option value="3">Option three</option>
</select>

The first option with an empty value (value="") is treated as a placeholder: its text is shown when nothing is selected, and by default it is not shown in the dropdown list. Only real choices appear when the menu opens.

Placeholders

Unlike input elements, select elements do not have a placeholder attribute, so many people use an option element with an empty (value="") as a placeholder. We ignore these options by default (see the previous example) and offer some alternative options for setting placeholder text.

Use custom placeholder text. Use data-bs-placeholder when you want specific label text while ignoring value-less options.

HTML
<label for="placeholderSelect" class="form-label">With explicit placeholder</label>
<select class="form-control"
        id="placeholderSelect"
        data-bs-toggle="custom-select"
        data-bs-placeholder="Pick a country…">
  <option value="">Ignored when data-bs-placeholder is set</option>
  <option value="us">United States</option>
  <option value="uk">United Kingdom</option>
  <option value="ca">Canada</option>
</select>

Show the empty options. With data-bs-hide-placeholder-option="false", empty options appear in the dropdown so users can re-select them to clear their choice.

HTML
<label for="showEmptySelect" class="form-label">Empty option in list</label>
<select class="form-control"
        id="showEmptySelect"
        data-bs-toggle="custom-select"
        data-bs-hide-placeholder-option="false">
  <option value="">None</option>
  <option value="a">Option A</option>
  <option value="b">Option B</option>
</select>

Option marked as placeholder. Use data-bs-placeholder="true" on an option (with or without a value) to hide it from the list while still using its text as the initial label.

HTML
<label for="optionPlaceholderSelect" class="form-label">Option as placeholder</label>
<select class="form-control"
        id="optionPlaceholderSelect"
        data-bs-toggle="custom-select">
  <option value="admin">Admin</option>
  <option value="editor">Editor</option>
  <option value="viewer">Viewer</option>
  <option value="" data-bs-placeholder="true">Choose a role…</option>
</select>

Custom content

Icons

Add inline SVG icons to options using the data-bs-icon attribute.

HTML
<label for="iconSelect" class="form-label">Select with icons</label>
<select class="form-control" id="iconSelect" data-bs-toggle="custom-select">
  <option value="">Choose a status…</option>
  <option value="pending" data-bs-icon="<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='currentColor' viewBox='0 0 16 16'><path d='M8 3.5a.5.5 0 0 0-1 0V9a.5.5 0 0 0 .252.434l3.5 2a.5.5 0 0 0 .496-.868L8 8.71z'/><path d='M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m7-8A7 7 0 1 1 1 8a7 7 0 0 1 14 0'/></svg>">Pending</option>
  <option value="approved" data-bs-icon="<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='currentColor' viewBox='0 0 16 16'><path d='M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16'/><path d='m10.97 4.97-.02.022-3.473 4.425-2.093-2.094a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-1.071-1.05'/></svg>">Approved</option>
  <option value="rejected" data-bs-icon="<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='currentColor' viewBox='0 0 16 16'><path d='M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16'/><path d='M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708'/></svg>">Rejected</option>
</select>

Descriptions

Add secondary descriptive text below option labels using data-bs-description.

HTML
<label for="descSelect" class="form-label">Select a plan</label>
<select class="form-control" id="descSelect" data-bs-toggle="custom-select">
  <option value="">Choose a plan…</option>
  <option value="starter" data-bs-description="For individuals and small projects">Starter</option>
  <option value="pro" data-bs-description="For growing teams and businesses">Professional</option>
  <option value="enterprise" data-bs-description="Custom solutions for large organizations">Enterprise</option>
</select>

Icons with descriptions

Combine icons and descriptions for rich option content.

HTML
<label for="richSelect" class="form-label">Notification preference</label>
<select class="form-control" id="richSelect" data-bs-toggle="custom-select">
  <option value="">Choose notification type…</option>
  <option value="email" data-bs-icon="<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='currentColor' viewBox='0 0 16 16'><path d='M0 4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm2-1a1 1 0 0 0-1 1v.217l7 4.2 7-4.2V4a1 1 0 0 0-1-1zm13 2.383-4.708 2.825L15 11.105zm-.034 6.876-5.64-3.471L8 9.583l-1.326-.795-5.64 3.47A1 1 0 0 0 2 13h12a1 1 0 0 0 .966-.741M1 11.105l4.708-2.897L1 5.383z'/></svg>" data-bs-description="Receive updates via email">Email notifications</option>
  <option value="sms" data-bs-icon="<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='currentColor' viewBox='0 0 16 16'><path d='M11 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1zM5 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2z'/><path d='M8 14a1 1 0 1 0 0-2 1 1 0 0 0 0 2'/></svg>" data-bs-description="Get SMS alerts to your phone">SMS notifications</option>
  <option value="push" data-bs-icon="<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='currentColor' viewBox='0 0 16 16'><path d='M8 16a2 2 0 0 0 2-2H6a2 2 0 0 0 2 2M8 1.918l-.797.161A4 4 0 0 0 4 6c0 .628-.134 2.197-.459 3.742-.16.767-.376 1.566-.663 2.258h10.244c-.287-.692-.502-1.49-.663-2.258C12.134 8.197 12 6.628 12 6a4 4 0 0 0-3.203-3.92zM14.22 12c.223.447.481.801.78 1H1c.299-.199.557-.553.78-1C2.68 10.2 3 6.88 3 6c0-2.42 1.72-4.44 4.005-4.901a1 1 0 1 1 1.99 0A5 5 0 0 1 13 6c0 .88.32 4.2 1.22 6'/></svg>" data-bs-description="Browser push notifications">Push notifications</option>
  <option value="none" data-bs-icon="<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='currentColor' viewBox='0 0 16 16'><path d='M5.164 14H15c-.299-.199-.557-.553-.78-1-.9-1.8-1.22-5.12-1.22-6q0-.396-.06-.776l-.938.938c.02.708.157 2.154.457 3.58.161.767.377 1.566.663 2.258H6.164zm5.581-9.91a4 4 0 0 0-1.948-1.01L8 2.917l-.797.161A4 4 0 0 0 4 7c0 .628-.134 2.197-.459 3.742q-.075.358-.166.718l-1.653 1.653q.03-.055.059-.113C2.679 11.2 3 7.88 3 7c0-2.42 1.72-4.44 4.005-4.901a1 1 0 1 1 1.99 0c.942.19 1.788.645 2.457 1.284zM10 15a2 2 0 1 1-4 0zM1.354 1.146a.5.5 0 0 0-.708.708l14 14a.5.5 0 0 0 .708-.708z'/></svg>" data-bs-description="Disable all notifications">No notifications</option>
</select>

Full custom HTML

For complete control, use data-bs-content to specify raw HTML content. This overrides all other data attributes.

HTML
<label for="htmlSelect" class="form-label">Priority level</label>
<select class="form-control" id="htmlSelect" data-bs-toggle="custom-select">
  <option value="">Select priority…</option>
  <option value="low" data-bs-content="<span class='badge text-bg-secondary'>Low</span> Not urgent">Low priority</option>
  <option value="medium" data-bs-content="<span class='badge text-bg-warning'>Medium</span> Normal priority">Medium priority</option>
  <option value="high" data-bs-content="<span class='badge text-bg-danger'>High</span> Needs attention">High priority</option>
</select>

Custom HTML content is sanitized by default for security. Use the allowHtml option with caution if you need to disable sanitization.

Enable filtering with the data-bs-live-search="true" attribute. Users can type to filter options by their text content, descriptions, and search tokens.

HTML
<label for="searchSelect" class="form-label">Search countries</label>
<select class="form-control" id="searchSelect" data-bs-toggle="custom-select" data-bs-live-search="true">
  <option value="">Type to search…</option>
  <option value="us">United States</option>
  <option value="uk">United Kingdom</option>
  <option value="ca">Canada</option>
  <option value="au">Australia</option>
  <option value="de">Germany</option>
  <option value="fr">France</option>
  <option value="jp">Japan</option>
  <option value="br">Brazil</option>
</select>

Search tokens

Add hidden keywords to options using data-bs-tokens to improve searchability without showing the keywords in the UI.

HTML
<label for="tokenSelect" class="form-label">Find a food item</label>
<select class="form-control" id="tokenSelect" data-bs-toggle="custom-select" data-bs-live-search="true">
  <option value="">Search for food…</option>
  <option value="hotdog" data-bs-tokens="american fast food">Hot Dog</option>
  <option value="burger" data-bs-tokens="american beef fast food">Hamburger</option>
  <option value="sushi" data-bs-tokens="japanese fish rice">Sushi</option>
  <option value="pizza" data-bs-tokens="italian cheese">Pizza</option>
  <option value="tacos" data-bs-tokens="mexican beef chicken">Tacos</option>
</select>

Option groups

Standard <optgroup> elements are rendered as section headers.

HTML
<label for="groupSelect" class="form-label">Select a vehicle</label>
<select class="form-control" id="groupSelect" data-bs-toggle="custom-select">
  <option value="">Choose a vehicle…</option>
  <optgroup label="Cars">
    <option value="sedan">Sedan</option>
    <option value="suv">SUV</option>
    <option value="sports">Sports Car</option>
  </optgroup>
  <optgroup label="Motorcycles">
    <option value="cruiser">Cruiser</option>
    <option value="sport">Sport Bike</option>
  </optgroup>
  <optgroup label="Trucks">
    <option value="pickup">Pickup Truck</option>
    <option value="semi">Semi Truck</option>
  </optgroup>
</select>

Sizing

Use .form-control-sm or .form-control-lg classes on the select element for different sizes.

HTML
<select class="form-control form-control-sm" data-bs-toggle="custom-select">
  <option value="">Small select</option>
  <option value="1">Option one</option>
  <option value="2">Option two</option>
</select>

<select class="form-control" data-bs-toggle="custom-select">
  <option value="">Default select</option>
  <option value="1">Option one</option>
  <option value="2">Option two</option>
</select>

<select class="form-control form-control-lg" data-bs-toggle="custom-select">
  <option value="">Large select</option>
  <option value="1">Option one</option>
  <option value="2">Option two</option>
</select>

Button styling

Style the toggle as a button instead of a form control by using button classes.

HTML
<select class="btn-outline theme-primary" data-bs-toggle="custom-select">
  <option value="">Outline button</option>
  <option value="1">Option one</option>
  <option value="2">Option two</option>
</select>

<select class="btn-solid theme-primary" data-bs-toggle="custom-select">
  <option value="">Solid button</option>
  <option value="1">Option one</option>
  <option value="2">Option two</option>
</select>

<select class="btn-subtle theme-secondary" data-bs-toggle="custom-select">
  <option value="">Subtle button</option>
  <option value="1">Option one</option>
  <option value="2">Option two</option>
</select>

Disabled states

Disabled select

Add the disabled attribute to the select element.

HTML
<select class="form-control" data-bs-toggle="custom-select" disabled>
  <option value="">Disabled select</option>
  <option value="1">Option one</option>
  <option value="2">Option two</option>
</select>

Disabled options

Individual options can be disabled.

HTML
<select class="form-control" data-bs-toggle="custom-select">
  <option value="">Choose an option…</option>
  <option value="1">Available option</option>
  <option value="2" disabled>Disabled option</option>
  <option value="3">Another available option</option>
</select>

Disabled option groups

Disable an entire optgroup to disable all options within it.

HTML
<select class="form-control" data-bs-toggle="custom-select">
  <option value="">Choose an option…</option>
  <optgroup label="Available">
    <option value="1">Option one</option>
    <option value="2">Option two</option>
  </optgroup>
  <optgroup label="Coming Soon" disabled>
    <option value="3">Option three</option>
    <option value="4">Option four</option>
  </optgroup>
</select>

Usage

Via data attributes

Add data-bs-toggle="custom-select" to your select element to automatically initialize. Options can be passed as data attributes using the data-bs- prefix.

HTML
<select class="form-control"
        data-bs-toggle="custom-select"
        data-bs-live-search="true"
        data-bs-live-search-placeholder="Find an option…">
  <option value="1">Option one</option>
  <option value="2">Option two</option>
</select>

Via JavaScript

Initialize manually with JavaScript:

JavaScript
const selectElement = document.querySelector('select')
const customSelect = new bootstrap.CustomSelect(selectElement, {
  liveSearch: true,
  showCheckmark: true
})

Options

Options can be passed via data attributes or JavaScript:

NameTypeDefaultDescription
hidePlaceholderOptionbooleantrueHide options with empty value or data-bs-placeholder="true" from the dropdown; they are used only for the toggle label.
liveSearchbooleanfalseEnable search input in the dropdown.
liveSearchPlaceholderstring'Search…'Placeholder text for the search input.
liveSearchNormalizebooleanfalseNormalize accented characters for search matching.
showCheckmarkbooleantrueShow a checkmark icon on selected options.
allowHtmlbooleanfalseAllow unsanitized HTML in data-bs-content.
sanitizebooleantrueSanitize HTML content to prevent XSS.
sanitizeFnfunction | nullnullCustom sanitization function.
allowListobjectDefault allowlistObject containing allowed HTML elements and attributes.
placementstring'bottom-start'Dropdown placement (uses Floating UI).
offsetarray[0, 2]Offset from the toggle [skidding, distance].
boundarystring | element'clippingParents'Overflow constraint boundary.

Data attributes on the select

AttributeDescription
data-bs-placeholderPlaceholder text shown when nothing is selected (overrides empty option text).
data-bs-hide-placeholder-optionWhen true (default), empty-value options are hidden from the list. Set to false to show them.

Data attributes on options

AttributeDescription
data-bs-placeholderWhen "true", treat this option as a placeholder: hidden from the list, used only for the initial label.
data-bs-contentFull custom HTML content (overrides other attributes).
data-bs-iconInline SVG HTML to display before the option text.
data-bs-imageImage URL to display before the option text.
data-bs-descriptionSecondary text displayed below the option label.
data-bs-tokensSpace-separated keywords for search matching.

Methods

MethodDescription
show()Opens the dropdown menu.
hide()Closes the dropdown menu.
toggle()Toggles the dropdown menu.
refresh()Rebuilds the menu from the current select options.
dispose()Destroys the component and restores the original select.
JavaScript
const selectElement = document.querySelector('select')
const customSelect = bootstrap.CustomSelect.getOrCreateInstance(selectElement)

// Open the dropdown
customSelect.show()

// Refresh after dynamically adding options
const newOption = document.createElement('option')
newOption.value = 'new'
newOption.textContent = 'New Option'
selectElement.appendChild(newOption)
customSelect.refresh()

Events

Events are fired on the original select element:

EventDescription
show.bs.custom-selectFired before the dropdown is shown.
shown.bs.custom-selectFired after the dropdown is shown.
hide.bs.custom-selectFired before the dropdown is hidden.
hidden.bs.custom-selectFired after the dropdown is hidden.
change.bs.custom-selectFired when the selection changes. Contains value and option properties.
JavaScript
const selectElement = document.querySelector('select')

selectElement.addEventListener('change.bs.custom-select', event => {
  console.log('Selected value:', event.value)
  console.log('Selected option:', event.option)
})

selectElement.addEventListener('shown.bs.custom-select', () => {
  console.log('Dropdown opened')
})

Keyboard navigation

The custom select supports full keyboard navigation:

When toggle is focused

KeyAction
Enter / SpaceOpen dropdown
/ Open dropdown and focus first/last item
EscapeClose dropdown

When dropdown is open

KeyAction
Move to next option
Move to previous option
HomeMove to first option
EndMove to last option
Enter / SpaceSelect focused option
EscapeClose dropdown and focus toggle
TabClose dropdown

When search input is focused

KeyAction
Move focus to first visible option
TypeFilter options in real-time

Accessibility

  • The toggle button has aria-haspopup="listbox" and aria-expanded attributes
  • The dropdown menu has role="listbox"
  • Each option has role="option" and aria-selected attributes
  • Disabled options have aria-disabled="true"
  • Full keyboard navigation support
  • Original select remains in DOM for screen readers and form submission

CSS

Variables

The custom select uses variables from form controls and dropdowns:

SCSS
// Form control variables
--control-min-height
--control-padding-y
--control-padding-x
--control-border-color
--control-border-radius
--control-select-bg  // Caret icon

// Dropdown variables
--dropdown-link-color
--dropdown-link-hover-bg
--dropdown-link-active-color
--dropdown-link-active-bg

Sass variables

Custom select styles are defined in scss/forms/_custom-select.scss and use variables from _form-variables.scss.