Combobox
An input with a dropdown list for searching and selecting from a set of options.
Interactive
Requires:
veritheme.js
JS init: automatic
Default
A searchable input with a dropdown list of options. Try searching and selecting.
html
<div class="vt-w-72 vt-relative vt-combobox-wrapper">
<div class="vt-relative">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="lucide lucide-search vt-absolute vt-left-3 vt-top-1/2 vt--translate-y-1/2 vt-w-4 vt-h-4 vt-text-foreground-muted">
<path d="m21 21-4.34-4.34"></path><circle cx="11" cy="11" r="8"></circle>
</svg>
<input type="text" placeholder="Search framework..." onfocus="openCombobox('cb-default')" oninput="filterCombobox(this, 'cb-default')" class="vt-input vt-pl-10" aria-label="Search framework...">
</div>
<div id="cb-default" class="vt-dropdown-menu vt-w-full vt-mt-1" data-combobox-content="">
<div class="vt-combobox-item" onclick="selectComboboxItem(this)">
<span class="vt-w-4 vt-h-4 vt-shrink-0 vt-combobox-icon"></span>
Next.js
</div>
<div class="vt-combobox-item" onclick="selectComboboxItem(this)">
<span class="vt-w-4 vt-h-4 vt-shrink-0 vt-combobox-icon"></span>
SvelteKit
</div>
<div class="vt-combobox-item" onclick="selectComboboxItem(this)">
<span class="vt-w-4 vt-h-4 vt-shrink-0 vt-combobox-icon"></span>
Nuxt
</div>
<div class="vt-combobox-item" onclick="selectComboboxItem(this)">
<span class="vt-w-4 vt-h-4 vt-shrink-0 vt-combobox-icon"></span>
Remix
</div>
<div class="vt-combobox-item vt-selected" onclick="selectComboboxItem(this)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="vt-w-4 vt-h-4 vt-shrink-0 vt-text-info-fill vt-combobox-icon"><path d="M20 6 9 17l-5-5"></path></svg>
Astro
</div>
<div class="vt-combobox-empty vt-hidden vt-py-4 vt-text-center vt-text-sm vt-text-foreground-muted">No results found.</div>
</div>
</div>
With Groups
Combobox with options organized into labeled groups. Icons are preserved on selection.
html
<div class="vt-w-72 vt-relative vt-combobox-wrapper" data-keep-icons="true">
<div class="vt-relative">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="lucide lucide-search vt-absolute vt-left-3 vt-top-1/2 vt--translate-y-1/2 vt-w-4 vt-h-4 vt-text-foreground-muted">
<path d="m21 21-4.34-4.34"></path><circle cx="11" cy="11" r="8"></circle>
</svg>
<input type="text" placeholder="Search..." onfocus="openCombobox('cb-groups')" oninput="filterCombobox(this, 'cb-groups')" class="vt-input vt-pl-10" aria-label="Search...">
</div>
<div id="cb-groups" class="vt-dropdown-menu vt-w-full vt-mt-1" data-combobox-content="">
<div class="vt-combobox-group" data-combobox-group="">
<div class="vt-text-xs vt-font-semibold vt-text-foreground-muted vt-px-2 vt-py-1.5">Suggestions</div>
<div class="vt-combobox-item" onclick="selectComboboxItem(this)">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="lucide lucide-calendar vt-w-4 vt-h-4 vt-shrink-0 vt-text-foreground-muted">
<path d="M8 2v4"></path><path d="M16 2v4"></path><rect width="18" height="18" x="3" y="4" rx="2"></rect><path d="M3 10h18"></path>
</svg>
Calendar
</div>
<div class="vt-combobox-item" onclick="selectComboboxItem(this)">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="lucide lucide-search vt-w-4 vt-h-4 vt-shrink-0 vt-text-foreground-muted">
<path d="m21 21-4.34-4.34"></path><circle cx="11" cy="11" r="8"></circle>
</svg>
Search
</div>
<div class="vt-combobox-item" onclick="selectComboboxItem(this)">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="lucide lucide-settings vt-w-4 vt-h-4 vt-shrink-0 vt-text-foreground-muted">
<path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915"></path><circle cx="12" cy="12" r="3"></circle>
</svg>
Settings
</div>
</div>
<div class="vt-h-[0.5px] vt-bg-border-default vt-my-1 vt--mx-1"></div>
<div class="vt-combobox-group" data-combobox-group="">
<div class="vt-text-xs vt-font-semibold vt-text-foreground-muted vt-px-2 vt-py-1.5">Results</div>
<div class="vt-combobox-item" onclick="selectComboboxItem(this)">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="lucide lucide-calculator vt-w-4 vt-h-4 vt-shrink-0 vt-text-foreground-muted">
<rect width="16" height="20" x="4" y="2" rx="2"></rect><line x1="8" x2="16" y1="6" y2="6"></line><line x1="16" x2="16" y1="14" y2="18"></line><path d="M16 10h.01"></path><path d="M12 10h.01"></path><path d="M8 10h.01"></path><path d="M12 14h.01"></path><path d="M8 14h.01"></path><path d="M12 18h.01"></path><path d="M8 18h.01"></path>
</svg>
Calculator
</div>
<div class="vt-combobox-item" onclick="selectComboboxItem(this)">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="lucide lucide-user vt-w-4 vt-h-4 vt-shrink-0 vt-text-foreground-muted">
<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle>
</svg>
Profile
</div>
</div>
<div class="vt-combobox-empty vt-hidden vt-py-4 vt-text-center vt-text-sm vt-text-foreground-muted">No results found.</div>
</div>
</div>
Multi-Select
Select multiple items with tags displayed in the input area.
html
<div class="vt-w-80 vt-relative vt-combobox-wrapper" data-multi="true">
<div class="vt-combobox-tags vt-flex vt-flex-wrap vt-items-center vt-gap-1.5 vt-min-h-[2.5rem] vt-w-full vt-rounded-lg vt-border vt-border-border-strong vt-bg-surface-card vt-px-3 vt-py-1.5 vt-cursor-text vt-focus-within:ring-1 vt-focus-within:ring-info-fill" onclick="this.querySelector('input').focus(); openCombobox('cb-multi')">
<span class="vt-combobox-tag vt-inline-flex vt-items-center vt-gap-1 vt-rounded vt-bg-surface-subtle vt-px-2 vt-py-0.5 vt-text-xs vt-font-medium" data-tag-value="React">
React
<button type="button" onclick="event.stopPropagation(); removeComboboxTag(this)" class="vt-ml-0.5 vt-rounded-sm vt-hover:bg-surface-moderate vt-transition-colors"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="vt-w-3 vt-h-3"><path d="M18 6 6 18"></path><path d="m6 6 12 12"></path></svg></button>
</span>
<span class="vt-combobox-tag vt-inline-flex vt-items-center vt-gap-1 vt-rounded vt-bg-surface-subtle vt-px-2 vt-py-0.5 vt-text-xs vt-font-medium" data-tag-value="Vue">
Vue
<button type="button" onclick="event.stopPropagation(); removeComboboxTag(this)" class="vt-ml-0.5 vt-rounded-sm vt-hover:bg-surface-moderate vt-transition-colors"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="vt-w-3 vt-h-3"><path d="M18 6 6 18"></path><path d="m6 6 12 12"></path></svg></button>
</span>
<input type="text" onfocus="openCombobox('cb-multi')" oninput="filterCombobox(this, 'cb-multi')" class="vt-flex-1 vt-min-w-[40px] vt-bg-transparent vt-text-sm vt-outline-none vt-placeholder:text-foreground-muted" aria-label="Value">
</div>
<div id="cb-multi" class="vt-dropdown-menu vt-w-full vt-mt-1" data-combobox-content="">
<div class="vt-combobox-item vt-selected" onclick="toggleMultiComboboxItem(this)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="vt-w-4 vt-h-4 vt-shrink-0 vt-text-info-fill vt-combobox-icon"><path d="M20 6 9 17l-5-5"></path></svg>
React
</div>
<div class="vt-combobox-item vt-selected" onclick="toggleMultiComboboxItem(this)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="vt-w-4 vt-h-4 vt-shrink-0 vt-text-info-fill vt-combobox-icon"><path d="M20 6 9 17l-5-5"></path></svg>
Vue
</div>
<div class="vt-combobox-item" onclick="toggleMultiComboboxItem(this)">
<span class="vt-w-4 vt-h-4 vt-shrink-0 vt-combobox-icon"></span>
Angular
</div>
<div class="vt-combobox-item" onclick="toggleMultiComboboxItem(this)">
<span class="vt-w-4 vt-h-4 vt-shrink-0 vt-combobox-icon"></span>
Svelte
</div>
<div class="vt-combobox-item" onclick="toggleMultiComboboxItem(this)">
<span class="vt-w-4 vt-h-4 vt-shrink-0 vt-combobox-icon"></span>
Solid
</div>
<div class="vt-combobox-empty vt-hidden vt-py-4 vt-text-center vt-text-sm vt-text-foreground-muted">No results found.</div>
</div>
</div>
With Button Trigger
Combobox with a button trigger instead of an input field.
html
<div class="vt-w-56 vt-relative vt-combobox-wrapper">
<button type="button" onclick="toggleCombobox('cb-button')" class="vt-input vt-items-center vt-justify-between vt-hover:bg-surface-subtle">
<span class="vt-combobox-button-label vt-text-foreground-muted">Select status...</span>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="lucide lucide-chevron-down vt-w-4 vt-h-4 vt-text-foreground-muted vt-shrink-0">
<path d="m6 9 6 6 6-6"></path>
</svg>
</button>
<div id="cb-button" class="vt-dropdown-menu vt-w-full vt-mt-1" data-combobox-content="">
<div class="vt-combobox-item" onclick="selectComboboxButton(this, 'cb-button')">
<span class="vt-w-4 vt-h-4 vt-shrink-0 vt-combobox-icon"></span>
Backlog
</div>
<div class="vt-combobox-item" onclick="selectComboboxButton(this, 'cb-button')">
<span class="vt-w-4 vt-h-4 vt-shrink-0 vt-combobox-icon"></span>
Todo
</div>
<div class="vt-combobox-item" onclick="selectComboboxButton(this, 'cb-button')">
<span class="vt-w-4 vt-h-4 vt-shrink-0 vt-combobox-icon"></span>
In Progress
</div>
<div class="vt-combobox-item" onclick="selectComboboxButton(this, 'cb-button')">
<span class="vt-w-4 vt-h-4 vt-shrink-0 vt-combobox-icon"></span>
Done
</div>
<div class="vt-combobox-item" onclick="selectComboboxButton(this, 'cb-button')">
<span class="vt-w-4 vt-h-4 vt-shrink-0 vt-combobox-icon"></span>
Cancelled
</div>
</div>
</div>
Guidelines
Searchable select, optionally multi-value with tags. Overkill for under ~10 options → select.
Do
Don't
Do
Don't
Anatomy
HTML structure of the Combobox component.
div.vt-combobox-wrapper ← Root (relative positioning)
├── input.vt-input ← Search input
└── div.vt-dropdown-menu ← Dropdown panel
├── div.vt-combobox-group ← Optional group
│ ├── div (label) ← Group heading
│ └── div.vt-command-item ← Option
│ ├── svg / span ← Check icon or spacer
│ └── text ← Option label
└── div.vt-combobox-empty ← No results message
API Reference
All CSS classes available for the Combobox component.
| Class | Type | Description |
|---|---|---|
vt-combobox-wrapper |
Base | Root wrapper — relative positioning context |
vt-combobox-item |
Base | Individual option in the dropdown list |
vt-combobox-group |
Modifier | Groups items under a label header |
vt-combobox-tags |
Modifier | Multi-select tag container wrapping the input |
vt-combobox-tag |
Modifier | Individual tag chip in multi-select mode |
vt-combobox-button-label |
Modifier | Label text inside button-trigger variant |
vt-combobox-empty |
Modifier | Empty state message when no results match |
vt-selected |
State | Marks the currently selected item |
vt-open |
State | Shows the dropdown panel |
data-multi |
Modifier | HTML attribute on wrapper — enables multi-select mode |
data-keep-icons |
Modifier | HTML attribute — preserves icons on selection |
Accessibility
Keyboard and screen reader support.
| Feature | Details |
|---|---|
| Focus | Dropdown opens on input focus, closes on click outside |
| Keyboard | Type to filter options in real-time |
| Selection | Click to select — checkmark icon indicates current selection |
| Multi-select | Tags display selected items, X button removes them |
| Empty state | Shows "No results found" when filter matches nothing |