Combobox
Searchable select input for filtering and selecting items from large datasets.
Overview
The Combobox component is a core building block of the Byteion design system, providing a robust, accessibility-compliant interface for its specific UI capabilities.
Why this exists
When a select has many options, a searchable combobox dramatically reduces selection time. It combines the discoverability of a dropdown with the precision of search.
Mental model
A combobox is a searchable select. Type to filter, arrow to navigate, Enter to select. It replaces a select when options exceed ~10.
When to use
When not to use
Basic example
The following example demonstrates standard usage, fully compliant with Byteion's token-first approach.
import { useState } from 'react';
import { Combobox } from '@byteion/react';
const vendors = [
{ value: 'apple', label: 'Apple' },
{ value: 'google', label: 'Google' },
{ value: 'meta', label: 'Meta' },
{ value: 'amazon', label: 'Amazon' },
];
export default function ComboboxDemo() {
const [value, setValue] = useState('google');
return (
<div className="grid max-w-md gap-4 p-6">
<Combobox label="Vendor" options={vendors} value={value} onChange={setValue} />
<Combobox label="Empty" options={vendors} placeholder="Search vendor" />
</div>
);
}
<script setup lang="ts">
import { ref } from 'vue';
import { Combobox } from '@byteion/vue';
const value = ref('google');
const vendors = [
{ value: 'apple', label: 'Apple' },
{ value: 'google', label: 'Google' },
{ value: 'meta', label: 'Meta' },
{ value: 'amazon', label: 'Amazon' },
];
</script>
<template>
<div class="grid max-w-md gap-4 p-6">
<Combobox v-model="value" label="Vendor" :options="vendors" />
<Combobox label="Empty" :options="vendors" placeholder="Search vendor" />
</div>
</template>
Variants
Each variant communicates a different meaning. Choose based on priority, not aesthetics.
Single default variant.
Sizes
Size should match the importance and context of the component.
- md — md size.
States
This component reacts seamlessly to the following interaction and data states:
- default — Resting appearance before interaction.
- hover — Pointer is over the element — visual feedback via color or shadow.
- focus — Keyboard or tab focus — shows ring indicator for accessibility.
- active — Currently selected or in use.
- disabled — Non-interactive — grayed out, cursor not-allowed.
- loading — Action in progress — spinner replaces icon, button is disabled.
- open — Component is expanded or visible.
- closed — Component is collapsed or hidden.
Anatomy
Usage guidelines
- Filter on input, debounce by 150ms.
- Highlight the first match automatically.
- Show an empty state when no results match.
Do and Don't
Do
- Debounce filtering.
- Auto-highlight first match.
- Show"No results" state.
Don't
- Do not filter on every keystroke without debounce.
- Do not close dropdown on no results.
- Do not use for small option lists.
Accessibility
Role combobox with aria-expanded, aria-activedescendant. Arrow keys navigate, Enter selects, Escape closes.
Role:combobox
ARIA Attributes
aria-expandedaria-controlsaria-activedescendant
Keyboard Navigation
- Click/focus: Opens the list without pre-highlighting the first option
- ArrowDown: Opens the list or moves the active option to the next item
- ArrowUp: Opens the list or moves the active option to the previous item
- Enter: Selects the active option when one exists
- Escape: Closes the overlay or cancels the action
Customization
Dropdown follows popover tokens.
Compose with React cva()
Use the exportedcva() +cn() utilities to compose your own variant on top of the Byteion tokens.
import { cva, cn, type VariantProps } from '@byteion/react';
const myCombobox = cva('base classes…', {
variants: {
accent: { soft:'border-primary-200', loud:'border-primary-500' },
},
defaultVariants: { accent:'soft' },
});
type Props = React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof myCombobox>;
Compose with Vue cva()
Use the exportedcva() +cn() utilities inside standard Vue setup blocks.
<script setup lang="ts">
import { computed, useAttrs } from 'vue';
import { cva, type VariantProps } from '@byteion/vue';
interface Props { accent?:'soft' |'loud' }
const props = withDefaults(defineProps<Props>(), { accent:'soft' });
const attrs = useAttrs();
const myCombobox = cva('base classes…', {
variants: { accent: { soft:'border-primary-200', loud:'border-primary-500' } },
});
const cls = computed(() => myCombobox({ accent: props.accent, class: attrs.class as string | undefined }));
</script>
Runtime events
Byteion keeps framework APIs idiomatic: HTML uses DOM custom events, React uses callback props, and Vue uses emitted events / v-model updates.
HTML / JS events
bi:combobox:closebi:combobox:errorbi:combobox:loadbi:combobox:openbi:combobox:searchbi:combobox:select
React callbacks
onValueChange(value: string | null) => void
Vue emits
update:modelValue[value: string]
Remote / AJAX
Remote search
Add data-bi-source to opt into AJAX search. The runtime keeps local filtering as the default, debounces requests, aborts stale requests, updates data-bi-async-state, and renders loading, empty, and error status rows in the listbox.
Attributes
data-bi-sourcedata-bi-search-paramdata-bi-min-charsdata-bi-debouncedata-bi-value-fielddata-bi-label-fielddata-bi-loading-textdata-bi-empty-textdata-bi-error-text
Runtime events
bi:combobox:searchbi:combobox:loadbi:combobox:errorbi:combobox:select
Accessibility: loading sets aria-busy="true"; empty states use role="status"; error states use alert/status messaging where the runtime renders remote status rows.
API Reference
Requires data-bi-component="combobox" for JS behavior.
HTML Data Attributes
No custom data attributes required.
React Props · ComboboxProps
Extracted directly fromreact/src/components/Combobox.tsx.
export interface ComboboxProps {
options: ComboboxOption[];
/** Controlled selected value. */
value?: string | null;
defaultValue?: string | null;
onValueChange?: (value: string | null) => void;
label?: ReactNode;
helperText?: ReactNode;
error?: ReactNode;
placeholder?: string;
/** Text shown when no matches are found. */
emptyMessage?: ReactNode;
disabled?: boolean;
/** Allow clearing the selection. */
clearable?: boolean;
/** Render the popup open initially. Useful for documentation showcases. */
defaultOpen?: boolean;
id?: string;
className?: string;
}
Vue Props · ComboboxProps
Extracted directly fromvue/src/components/Combobox.vue.
export interface ComboboxProps {
modelValue?: string;
defaultValue?: string;
options: ComboboxOption[];
placeholder?: string;
label?: string;
helperText?: string;
error?: string;
disabled?: boolean;
defaultOpen?: boolean;
emptyMessage?: string;
id?: string;
}
Developer tips
Common mistakes
Production usage tips
data-bi-* attributes instead of styling hooks.
Premium examples
Advanced patterns available in Byteion Pro:
Related components
Unlock Premium Patterns
Get access to copy-paste ready Pro blocks, SaaS templates, and advanced data-tables built with this component.
Explore Pro Components