Skip to content

Combobox

A searchable dropdown (autocomplete) that lets users filter and select from a list of options with full keyboard navigation and accessibility support.

---
import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList } from '@bejamas/ui/components/combobox';
---
<Combobox class="w-[200px]">
<ComboboxInput placeholder="Select framework..." showClear />
<ComboboxContent>
<ComboboxList>
<ComboboxEmpty>No results found.</ComboboxEmpty>
<ComboboxItem value="astro">Astro</ComboboxItem>
<ComboboxItem value="next">Next.js</ComboboxItem>
<ComboboxItem value="nuxt">Nuxt</ComboboxItem>
<ComboboxItem value="remix">Remix</ComboboxItem>
<ComboboxItem value="svelte">SvelteKit</ComboboxItem>
</ComboboxList>
</ComboboxContent>
</Combobox>
Terminal window
bunx bejamas add combobox
---
import {
Combobox,
ComboboxInput,
ComboboxContent,
ComboboxList,
ComboboxItem,
ComboboxEmpty,
ComboboxGroup,
ComboboxLabel,
ComboboxSeparator,
} from '@bejamas/ui/components/combobox';
---
<Combobox name="framework">
<ComboboxInput placeholder="Select framework..." />
<ComboboxContent>
<ComboboxList>
<ComboboxEmpty>No results found.</ComboboxEmpty>
<ComboboxItem value="astro">Astro</ComboboxItem>
<ComboboxItem value="next">Next.js</ComboboxItem>
</ComboboxList>
</ComboboxContent>
</Combobox>

Use the showClear prop to show a clear button in the input.

---
import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList } from '@bejamas/ui/components/combobox';
---
<Combobox class="w-[200px]">
<ComboboxInput placeholder="Select framework..." showClear />
<ComboboxContent>
<ComboboxList>
<ComboboxEmpty>No results found.</ComboboxEmpty>
<ComboboxItem value="astro">Astro</ComboboxItem>
<ComboboxItem value="next">Next.js</ComboboxItem>
<ComboboxItem value="nuxt">Nuxt</ComboboxItem>
<ComboboxItem value="remix">Remix</ComboboxItem>
</ComboboxList>
</ComboboxContent>
</Combobox>

Use the size prop on ComboboxInput to match button/input sizing.

---
import { Combobox, ComboboxContent, ComboboxInput, ComboboxItem, ComboboxList } from '@bejamas/ui/components/combobox';
---
<div class="grid w-full max-w-sm gap-3">
<Combobox>
<ComboboxInput size="sm" placeholder="Small (h-8)" />
<ComboboxContent>
<ComboboxList>
<ComboboxItem value="astro-sm">Astro</ComboboxItem>
<ComboboxItem value="next-sm">Next.js</ComboboxItem>
</ComboboxList>
</ComboboxContent>
</Combobox>
<Combobox>
<ComboboxInput placeholder="Default (h-9)" />
<ComboboxContent>
<ComboboxList>
<ComboboxItem value="astro-default">Astro</ComboboxItem>
<ComboboxItem value="next-default">Next.js</ComboboxItem>
</ComboboxList>
</ComboboxContent>
</Combobox>
<Combobox>
<ComboboxInput size="lg" placeholder="Large (h-10)" />
<ComboboxContent>
<ComboboxList>
<ComboboxItem value="astro-lg">Astro</ComboboxItem>
<ComboboxItem value="next-lg">Next.js</ComboboxItem>
</ComboboxList>
</ComboboxContent>
</Combobox>
</div>

Use the ComboboxGroup and ComboboxLabel to group items and add a label to the group.

---
import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxLabel, ComboboxList, ComboboxSeparator } from '@bejamas/ui/components/combobox';
---
<Combobox class="w-[200px]">
<ComboboxInput placeholder="Select framework..." />
<ComboboxContent>
<ComboboxList>
<ComboboxEmpty>No results found.</ComboboxEmpty>
<ComboboxGroup>
<ComboboxLabel>Frontend</ComboboxLabel>
<ComboboxItem value="astro">Astro</ComboboxItem>
<ComboboxItem value="next">Next.js</ComboboxItem>
<ComboboxItem value="nuxt">Nuxt</ComboboxItem>
</ComboboxGroup>
<ComboboxSeparator />
<ComboboxGroup>
<ComboboxLabel>Backend</ComboboxLabel>
<ComboboxItem value="express">Express</ComboboxItem>
<ComboboxItem value="hono">Hono</ComboboxItem>
</ComboboxGroup>
</ComboboxList>
</ComboboxContent>
</Combobox>

Use the aria-invalid="true" attribute on the ComboboxInput to mark the combobox as invalid.

---
import { Combobox, ComboboxContent, ComboboxInput, ComboboxItem, ComboboxList } from '@bejamas/ui/components/combobox';
---
<Combobox class="w-[200px]">
<ComboboxInput placeholder="Select framework..." aria-invalid="true" />
<ComboboxContent>
<ComboboxList>
<ComboboxItem value="astro">Astro</ComboboxItem>
<ComboboxItem value="next">Next.js</ComboboxItem>
</ComboboxList>
</ComboboxContent>
</Combobox>

Use the disabled prop to disable the combobox.

---
import { Combobox, ComboboxContent, ComboboxInput, ComboboxItem, ComboboxList } from '@bejamas/ui/components/combobox';
---
<Combobox disabled class="w-[200px]">
<ComboboxInput placeholder="Select framework..." disabled />
<ComboboxContent>
<ComboboxList>
<ComboboxItem value="astro">Astro</ComboboxItem>
<ComboboxItem value="next">Next.js</ComboboxItem>
</ComboboxList>
</ComboboxContent>
</Combobox>

Use the autoHighlight prop to automatically highlight the first item on filter.

---
import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList } from '@bejamas/ui/components/combobox';
---
<Combobox autoHighlight class="w-[200px]">
<ComboboxInput placeholder="Start typing..." />
<ComboboxContent>
<ComboboxList>
<ComboboxEmpty>No results found.</ComboboxEmpty>
<ComboboxItem value="astro">Astro</ComboboxItem>
<ComboboxItem value="next">Next.js</ComboboxItem>
<ComboboxItem value="nuxt">Nuxt</ComboboxItem>
<ComboboxItem value="remix">Remix</ComboboxItem>
</ComboboxList>
</ComboboxContent>
</Combobox>

You can trigger the combobox from a button or any other component by using the render prop. Move the ComboboxInput inside the ComboboxContent.

---
import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList, ComboboxTrigger, ComboboxValue } from '@bejamas/ui/components/combobox';
---
<Combobox class="w-[200px]">
<ComboboxTrigger>
<ComboboxValue placeholder="Select country..." />
</ComboboxTrigger>
<ComboboxContent>
<ComboboxInput showTrigger={false} placeholder="Search countries..." />
<ComboboxList>
<ComboboxEmpty>No countries found.</ComboboxEmpty>
<ComboboxItem value="brazil" label="Brazil">Brazil</ComboboxItem>
<ComboboxItem value="canada" label="Canada">Canada</ComboboxItem>
<ComboboxItem value="france" label="France">France</ComboboxItem>
<ComboboxItem value="germany" label="Germany">Germany</ComboboxItem>
<ComboboxItem value="japan" label="Japan">Japan</ComboboxItem>
</ComboboxList>
</ComboboxContent>
</Combobox>

The combobox emits custom events that you can listen to:

EventDetailDescription
combobox:change{ value: string | null }Fired when a new value is selected
combobox:open-change{ open: boolean }Fired when the dropdown opens or closes
combobox:input-change{ inputValue: string }Fired when the user types in the input
<Combobox id="my-combobox">
<ComboboxInput placeholder="Search..." />
<ComboboxContent>
<ComboboxList>
<ComboboxItem value="option1">Option 1</ComboboxItem>
<ComboboxItem value="option2">Option 2</ComboboxItem>
</ComboboxList>
</ComboboxContent>
</Combobox>
<script>
const combobox = document.getElementById('my-combobox');
combobox.addEventListener('combobox:change', (e) => {
console.log('Selected value:', e.detail.value);
});
combobox.addEventListener('combobox:input-change', (e) => {
console.log('Input text:', e.detail.inputValue);
});
</script>

You can control the combobox programmatically by dispatching a combobox:set event:

const combobox = document.getElementById('my-combobox');
// Set a specific value
combobox.dispatchEvent(new CustomEvent('combobox:set', {
detail: { value: 'option2' }
}));
// Clear the value
combobox.dispatchEvent(new CustomEvent('combobox:set', {
detail: { value: null }
}));
// Open the dropdown
combobox.dispatchEvent(new CustomEvent('combobox:set', {
detail: { open: true }
}));
// Set the input text
combobox.dispatchEvent(new CustomEvent('combobox:set', {
detail: { inputValue: 'search term' }
}));
// Custom display text for selected value
// itemToStringValue — (item: HTMLElement | null, value: string | null) => string
// Controls what text appears in the input after selection.
// Useful when items contain rich content (icons, descriptions).
combobox.dispatchEvent(new CustomEvent('combobox:set', {
detail: {
itemToStringValue: (item, value) => value ? `TZ: ${value}` : ''
}
}));

The combobox sets these data attributes that you can use for styling or querying state:

AttributeElementDescription
data-statecombobox, combobox-contentCurrent state (open or closed)
data-valuecomboboxCurrently selected value
data-selectedcombobox-itemPresent when item is selected
data-highlightedcombobox-itemPresent when item is focused/highlighted
data-disabledcombobox, combobox-itemPresent when disabled
data-emptycombobox-contentPresent when no items match the filter

Placement options are configured on ComboboxContent:

<Combobox>
<ComboboxInput />
<ComboboxContent side="top" align="start" sideOffset={8}>
...
</ComboboxContent>
</Combobox>