Skip to main content
Vuetify0 is now in beta!
Vuetify0 Logo
Theme
Mode
Palettes
Accessibility
Vuetify One
Sign in to Vuetify One

Access premium tools across the Vuetify ecosystem — Bin, Play, Studio, and more.

Not a subscriber? See what's included

createCombobox

Low-level combobox coordinator for custom implementations. Most users should use the Combobox component instead.

Usage

ts
import { createCombobox } from '@vuetify/v0'

const combobox = createCombobox({ strict: true })

// Register items with the underlying selection
combobox.selection.register({ id: 'apple', value: 'Apple' })
combobox.selection.register({ id: 'banana', value: 'Banana' })
combobox.selection.register({ id: 'cherry', value: 'Cherry' })

// Open the dropdown
combobox.open()

// Select an item — in single mode this updates the query and closes
combobox.select('banana')
// combobox.query.value === 'Banana'
// combobox.isOpen.value === false

// Filter is pristine after selection — all items still visible
// combobox.pristine.value === true

// Once user types, the filter activates
combobox.query.value = 'ch'
// combobox.pristine.value === false
// combobox.filtered.value === Set { 'cherry' }

Context / DI

Context Object

createCombobox returns a ComboboxContext with the following API surface:

MemberTypeDescription
open()() => voidOpens the dropdown
close()() => voidCloses; applies strict revert if needed
toggle()() => voidOpens or closes
select(id)(id: ID) => voidSelects an item by ID
clear()() => voidResets query and deselects all
idstringBase ID used for ARIA relationships
inputIdstring${id}-input
listboxIdstring${id}-listbox
multiplebooleanResolved multiple flag
strictMaybeRefOrGetter<boolean>Strict option ref
disabledMaybeRefOrGetter<boolean>Disabled option ref
namestring | undefinedForm field name
formstring | undefinedAssociated form ID

Dependency Injection

Use createComboboxContext to get a DI-aware trinity for component-based setups:

ts
import { createComboboxContext, useCombobox } from '@vuetify/v0'

// In a Root component
const [useMyCombobox, provideMyCombobox, context] = createComboboxContext({
  namespace: 'my-combobox',
  strict: true,
})
provideMyCombobox(context)

// In any child component
const combobox = useCombobox('my-combobox')

useCombobox(namespace?) injects the nearest combobox context (default namespace: 'v0:combobox').

Adapters

Adapters extend ComboboxAdapter and translate a reactive query into a filtered ID set.

ts
abstract class ComboboxAdapter {
  abstract setup (context: ComboboxAdapterContext): ComboboxAdapterResult
}

interface ComboboxAdapterResult {
  filtered: Ref<Set<ID>>          // IDs that should be visible
  isLoading: ShallowRef<boolean>  // shows loading state in the UI
  isEmpty: Ref<boolean>           // true when no items match the query
}

The context exposes query (the current search string), selection (the underlying selection context), and items (all registered IDs). Return the three refs above and the combobox wires them to the dropdown state automatically.

ClientComboboxAdapter

The default. Filters registered items locally using substring matching (case-insensitive). Pass custom filter options to override the matching logic:

ts
import { ClientComboboxAdapter, createCombobox } from '@vuetify/v0'

const combobox = createCombobox({
  adapter: new ClientComboboxAdapter({
    filter: (query, value) => String(value).toLowerCase().startsWith(query.toLowerCase()),
  }),
})

ServerComboboxAdapter

A pass-through adapter that shows all registered items and sets isLoading to false. Use this when filtering is performed server-side — watch combobox.query to drive your own fetch:

ts
import { ServerComboboxAdapter, createCombobox, useCombobox } from '@vuetify/v0'
import { watch } from 'vue'

const combobox = createCombobox({ adapter: new ServerComboboxAdapter() })

// In a component that injects the context:
const { query } = useCombobox()

watch(query, async q => {
  const results = await fetch(`/api/search?q=${q}`).then(r => r.json())
  // Update items via combobox.selection.register / unregister
})
Tip

See the Combobox server example for a complete integration.

Architecture

createCombobox orchestrates four independent primitives without extending their chains — it composes them. The adapter translates queries into a filtered set; virtual focus uses that set to skip hidden items.

createCombobox Architecture

Use controls to zoom and pan. Click outside or press Escape to close.

createCombobox Architecture

Options

ts
interface ComboboxOptions {
  multiple?: MaybeRefOrGetter<boolean>   // Enable multi-select
  mandatory?: MaybeRefOrGetter<boolean>  // Prevent deselecting last item
  disabled?: MaybeRefOrGetter<boolean>   // Disable all interaction
  strict?: MaybeRefOrGetter<boolean>     // Revert query on close if no match
  adapter?: ComboboxAdapter              // Filtering strategy (default: ClientComboboxAdapter)
  displayValue?: (value: unknown) => string  // Format selected value for display in input
  id?: string                            // Base ID for ARIA attributes
  name?: string                          // Hidden input name for form submission
  form?: string                          // Associated form ID
}

Reactivity

PropertyTypeReactiveNotes
queryShallowRef<string>Current input text
pristineShallowRef<boolean>true after selection; false once user types
filteredRef<Set<ID>>IDs that pass the current filter
isEmptyRef<boolean>true when filtered set is empty
isLoadingShallowRef<boolean>Forwarded from adapter
isOpenShallowRef<boolean>Popover open state
selectionSelectionContextFull selection API
popoverPopoverReturnPopover positioning API
cursorVirtualFocusReturnKeyboard focus API
inputElShallowRef<HTMLElement | null>Reference to the <input> element

Examples

Country Autocomplete

A fully custom country picker built directly on createCombobox with the default ClientComboboxAdapter. The composable registers a dozen countries into the underlying selection registry; the adapter filters the visible set on every keystroke, and cursor (the useVirtualFocus surface) tracks the keyboard-highlighted row independently of real DOM focus. A separate panel mirrors the confirmed selection, so the example shows both halves of an autocomplete: the typeahead input and the value display it feeds.

State and view are split deliberately. useCountrySearch owns the data and the coordinated state — it returns the combobox context plus a selected getter derived from selection.selectedIds — while CountryAutocomplete owns the markup and the DOM events. The composable never touches events; the component wires onInput, onKeydown, and @focus to drive the context, exactly mirroring how createCombobox expects a host to feed it. Arrow keys call cursor.next() / cursor.prev(), Enter reads cursor.highlightedId and routes to select(id), and Escape calls close(). Closing resets query and pristine, so the display getter falls back to the last selected ticket — the input reverts to the confirmed selection without any extra option.

ARIA wiring is manual but mechanical: role="combobox", aria-controls, aria-expanded, aria-autocomplete, and aria-activedescendant are set on the input from the IDs the context vends (inputId, listboxId, id), and each option row carries role="option", aria-selected, and a stable per-option id so screen readers can correlate the highlighted row. Reach for this approach when you need full control over the markup; prefer the Combobox component when the defaults suffice, and see createSelection for the selection layer underneath.

FileRole
useCountrySearch.tsCreates the combobox, registers countries, derives the selected country
CountryAutocomplete.vueRenders the input and listbox; owns the keyboard and input events
country-autocomplete.vueWires the composable to the component and shows the selected-value panel

Selected country

None yet — start typing to search.

Recipes

Strict Mode

When strict: true, closing the dropdown without an active selection reverts the query:

  • If an item is selected, query resets to that item’s label.

  • If nothing is selected, query resets to ''.

Non-strict mode (default) leaves whatever text the user typed in place.

Tip

aria-autocomplete="both" is set automatically on the input when strict is enabled, per the WAI-ARIA combobox pattern.

Pristine Flag

pristine tracks whether the query reflects the current selection or is a live filter:

  • Starts as true (no user input yet).

  • Becomes false when the user types — the adapter receives the raw query.

  • Resets to true after a selection (select(id)), so reopening the dropdown always shows all items instead of the previous typed query.

ts
// The adapter receives `search`, not `query` directly
const search = toRef(() => pristine.value ? '' : query.value)
const { filtered } = adapter.setup({ query: search, items })

Multi-Select Behavior

In multiple mode, select(id) differs from single mode:

  • Toggles the item (select → deselect on second click) via selection.toggle().

  • Clears the query so the user can search for the next item.

  • Keeps the dropdown open.

  • Highlights the clicked item via cursor.highlight(id) so ArrowDown continues from that position.

  • Refocuses the input so keyboard navigation continues immediately.

FAQ

Discord
Need help? Join our community for support and discussions ↗

API Reference

The following API details are for the createCombobox composable.
Was this page helpful?

Ctrl+/