Forms
Searchable select
Implement the searchable combobox with complete Boobstrap JS, Alpine.js, and React examples.
Forms
Searchable select
The combobox filters human-readable labels while synchronizing the selected machine value into a hidden form input.
Boobstrap JavaScript
HTML · Complete Boobstrap JS markup
<form>
<div class="bs-form-group">
<label class="bs-label" for="team-role">Team role</label>
<div class="bs-combobox" data-bs-combobox>
<input
class="bs-combobox-input"
id="team-role"
data-bs-combobox-input
placeholder="Search roles"
/>
<button
class="bs-combobox-toggle"
type="button"
data-bs-combobox-toggle
aria-label="Toggle roles"
></button>
<input type="hidden" name="team_role" data-bs-combobox-value />
<div class="bs-combobox-listbox" data-bs-combobox-listbox hidden>
<div class="bs-combobox-option" data-bs-combobox-option data-bs-value="designer">
Designer
</div>
<div class="bs-combobox-option" data-bs-combobox-option data-bs-value="engineer">
Engineer
</div>
<div class="bs-combobox-option" data-bs-combobox-option data-bs-value="product-manager">
Product manager
</div>
<div class="bs-combobox-option" data-bs-combobox-option data-bs-value="founder">
Founder
</div>
<div class="bs-combobox-empty" data-bs-combobox-empty hidden>
No roles found
</div>
</div>
</div>
</div>
</form>JavaScript · Initialize only comboboxes
import "@boobstrap/boobstrap";
import { initComboboxes } from "@boobstrap/boobstrap/js/combobox";
const comboboxes = initComboboxes(document);
// Call this from your router or view cleanup hook.
export function destroyComboboxes() {
comboboxes.forEach((combobox) => combobox.destroy());
}Alpine.js
JavaScript · Alpine setup
import "@boobstrap/boobstrap";
import Alpine from "alpinejs";
import boobstrap from "@boobstrap/alpine";
Alpine.plugin(boobstrap);
Alpine.start();HTML · Complete Alpine combobox
<form>
<div class="bs-form-group">
<label class="bs-label" for="alpine-role">Team role</label>
<div class="bs-combobox" x-data="bsCombobox" x-bind="root">
<input
class="bs-combobox-input"
id="alpine-role"
x-bind="input"
data-bs-combobox-input
placeholder="Search roles"
/>
<button
class="bs-combobox-toggle"
x-bind="toggleButton"
data-bs-combobox-toggle
aria-label="Toggle roles"
></button>
<input type="hidden" name="team_role" data-bs-combobox-value />
<div
class="bs-combobox-listbox"
x-bind="listbox"
data-bs-combobox-listbox
hidden
>
<div
class="bs-combobox-option"
x-bind="option"
data-bs-combobox-option
data-bs-value="designer"
>Designer</div>
<div
class="bs-combobox-option"
x-bind="option"
data-bs-combobox-option
data-bs-value="engineer"
>Engineer</div>
<div
class="bs-combobox-option"
x-bind="option"
data-bs-combobox-option
data-bs-value="product-manager"
>Product manager</div>
<div class="bs-combobox-empty" data-bs-combobox-empty hidden>
No roles found
</div>
</div>
</div>
</div>
</form>React
JSX · Complete React component
import "@boobstrap/boobstrap";
import { useCombobox } from "@boobstrap/react";
const roles = [
{ value: "designer", label: "Designer" },
{ value: "engineer", label: "Engineer" },
{ value: "product-manager", label: "Product manager" },
{ value: "founder", label: "Founder" },
];
export function TeamRoleSelect() {
const role = useCombobox({ options: roles });
return (
<div className="bs-form-group">
<label className="bs-label" htmlFor="react-role">Team role</label>
<div className="bs-combobox" {...role.getRootProps()}>
<input
className="bs-combobox-input"
id="react-role"
placeholder="Search roles"
{...role.getInputProps()}
/>
<button
className="bs-combobox-toggle"
{...role.getToggleProps({ "aria-label": "Toggle roles" })}
/>
<input type="hidden" name="team_role" value={role.value} readOnly />
<div className="bs-combobox-listbox" {...role.getListboxProps()}>
{role.filteredOptions.map((option, index) => (
<div
className="bs-combobox-option"
key={option.value}
{...role.getOptionProps(option, index)}
>
{option.label}
</div>
))}
{role.filteredOptions.length === 0 && (
<div className="bs-combobox-empty">No roles found</div>
)}
</div>
</div>
</div>
);
}Vue
Vue · Complete searchable select
<script setup>
import "@boobstrap/boobstrap";
import { useCombobox } from "@boobstrap/vue";
const roles = [
{ value: "designer", label: "Designer" },
{ value: "engineer", label: "Engineer" },
{ value: "product-manager", label: "Product manager" },
{ value: "founder", label: "Founder" },
];
const role = useCombobox({ options: roles });
</script>
<template>
<div class="bs-form-group">
<label class="bs-label" for="vue-role">Team role</label>
<div class="bs-combobox" v-bind="role.getRootProps()">
<input class="bs-combobox-input" id="vue-role" placeholder="Search roles"
v-bind="role.getInputProps()" />
<button class="bs-combobox-toggle"
v-bind="role.getToggleProps({ 'aria-label': 'Toggle roles' })"></button>
<input type="hidden" name="team_role" :value="role.value" />
<div class="bs-combobox-listbox" v-bind="role.getListboxProps()">
<div v-for="(option, index) in role.filteredOptions" :key="option.value"
class="bs-combobox-option"
v-bind="role.getOptionProps(option, index)">
{{ option.label }}
</div>
<div v-if="role.filteredOptions.length === 0" class="bs-combobox-empty">
No roles found
</div>
</div>
</div>
</div>
</template>Invalid combobox state
Engineering
Design
Marketing
No departments found
HTML · Invalid combobox
<div class="bs-form-group">
<label class="bs-label" for="department">
Department <span class="bs-label-required" aria-hidden="true">*</span>
</label>
<div class="bs-combobox" data-bs-combobox>
<input
class="bs-combobox-input bs-is-invalid"
id="department"
data-bs-combobox-input
placeholder="Search departments"
aria-invalid="true"
aria-describedby="dept-error"
required
/>
<button class="bs-combobox-toggle" type="button" data-bs-combobox-toggle aria-label="Toggle departments"></button>
<input type="hidden" name="department" data-bs-combobox-value />
<div class="bs-combobox-listbox" data-bs-combobox-listbox hidden>
<div class="bs-combobox-option" data-bs-combobox-option data-bs-value="engineering">Engineering</div>
<div class="bs-combobox-option" data-bs-combobox-option data-bs-value="design">Design</div>
<div class="bs-combobox-option" data-bs-combobox-option data-bs-value="marketing">Marketing</div>
<div class="bs-combobox-empty" data-bs-combobox-empty hidden>No departments found</div>
</div>
</div>
<span class="bs-form-feedback bs-form-feedback-invalid" id="dept-error">
Please select a valid department from the directory.
</span>
</div>Choose one owner: initialize the plain JavaScript controller, Alpine adapter, React hook, or Vue composable—not more than one on the same combobox. See the React and Vue adapter guides for controlled state and SSR rules.
Searchable Select (Combobox) API
Combobox components assemble search inputs, toggle triggers, floating listbox dropdowns, options, and empty states.
| Class / Data Attribute | Purpose |
|---|---|
.bs-combobox | Relative container coordinating input, toggle button, and floating listbox. |
.bs-combobox-input | Searchable text field with custom autocomplete, focus states, and trailing action space. |
.bs-combobox-toggle | Button trigger with animated caret indicating open and closed listbox state. |
.bs-combobox-listbox | Floating dropdown panel holding selectable option items and empty state messages. |
.bs-combobox-option | Selectable option row supporting active hover, selected checkmarks, and keyboard focus. |
.bs-combobox-empty | Fallback message displayed inside the listbox when search query matches zero options. |
data-bs-combobox | Initializes the vanilla JavaScript combobox controller on the container. |
data-bs-combobox-value | Hidden input field synchronized with the selected option value for form submission. |
.bs-form-feedback-invalid | Error message text describing invalid selection or required field constraints. |