Hacktron Design System

Combobox

A dropdown with a search box for picking one or more values from a list.

<template>  <Combobox :items="[{ value: 'a', label: 'Apple' }]" model-value="a" placeholder="Fruits" /></template>

Props

Name Type Default
items({ type: 'label'; label: string } | { value: string; label: string; description?: string; icon?: string; sublabel?: string; disabled?: boolean })[]-
placeholderstring-
searchPlaceholderstring-
iconstring-
multiplebooleanfalse
size'md' | 'sm' | 'xs''md'
errorbooleanfalse
virtualizebooleanfalse
modelValuestring | string[]-

Slots

Name Description
item-leadingScoped `{ item }`. Replaces an option row's leading icon (e.g. a status dot). Not rendered for section-header entries.
item-labelScoped `{ item }`. Replaces an option row's label (e.g. a status badge that carries its own text). Not rendered for section-header entries.

Guidelines

Do

  • Use this when the user needs to search or filter a list that's too long to scan by eye - a repository, an organization, a branch.
  • Use multiple when more than one option can be selected - the trigger keeps showing placeholder as a fixed label, with a small count badge appended once anything's selected.
  • When that placeholder is an instruction ("Select repositories"), pass selected-label with the field's name ("Repositories"): once anything is selected the trigger reads the name beside the count instead of the instruction.
  • Give an option a description when its label alone doesn't identify it (a display name plus a slug) - search matches the label and the description, so typing either finds it.
  • Use the #item-leading="{ item }" slot to replace an option's leading icon with custom content, such as a status dot. Without the slot, each item's own icon renders as usual. #item-label="{ item }" replaces the label the same way, for a badge that carries its own text.
  • In single mode (no multiple), picking the already-selected option again clears the value back to undefined, so a single-select Combobox can always be unset.
  • Set virtualize for a list of hundreds of options, so the panel renders only the rows in view.
  • Group related options with a { type: 'label', label: '...' } section header entry - but only once there are 2+ groups to tell apart.
  • Inside a UFormField, a validation error puts the field on the error surface by itself. Pass error for a state the form doesn't know; the trigger takes Input's red wash and border either way.
  • Use #item-trailing="{ item }" for right-hand content such as a project key; it replaces the item's sublabel.
  • Use #empty="{ searchTerm }" for a remote search's loading or error line, so the panel never shows a misleading "No results".
  • To page a remote list in on scroll, put a template ref on the Combobox and pass its viewportRef to useInfiniteScroll.
  • Write the placeholder by where the picker sits: in a filter or toolbar with no visible label, the bare field name ("Repository"); in a form under a label, "Select a/an x" ("Select a channel"). Once something is picked it shows the value, or with multiple the field name and the count (selected-label).
  • Always pass a placeholder when the picker can start empty: there's no built-in default.
  • Don't use this when the list is already short enough to scan without searching - use Select instead, which has no search box to fight.
  • Don't use this for a short, fully-visible list of mutually-exclusive options - use RadioGroup instead.
  • Don't use this for independent on/off choices - use Checkbox.
  • Don't add a section header to a single, ungrouped list - a lone { type: 'label' } entry has nothing to distinguish it from, so it just adds noise above the options.
  • Don't pass :ui - it would replace the whole design-system look (trigger, panel and option rows), so the type-check rejects it. Select and Combobox triggers share one resting surface with the secondary Button, and every picker shares one option-row style.
  • Don't use an example name ("e.g. acme/web"), an ellipsis, or a generic "Select something" as the placeholder: an example reads as already picked, and a generic one doesn't say what to pick.

Examples

Multiple select

Selecting more than one item, with search to narrow a long list.

<template>  <Combobox    v-model="selected"    :items="[      { value: 'apple', label: 'Apple' },      { value: 'banana', label: 'Banana' },      { value: 'blueberry', label: 'Blueberry' },      { value: 'grapes', label: 'Grapes' },      { value: 'pineapple', label: 'Pineapple' },    ]"    multiple    placeholder="Fruits"  /></template>

Sizes

The three trigger height steps - xs, sm, and md.

<template>  <Combobox v-model="selected" :items="items" size="xs" placeholder="Fruits" />  <Combobox v-model="selected" :items="items" size="sm" placeholder="Fruits" />  <Combobox v-model="selected" :items="items" size="md" placeholder="Fruits" /></template>

Multiple select sizes

The count badge checked at every trigger size, not just the default.

<template>  <Combobox v-model="selected" :items="items" multiple size="xs" placeholder="Fruits" />  <Combobox v-model="selected" :items="items" multiple size="sm" placeholder="Fruits" />  <Combobox v-model="selected" :items="items" multiple size="md" placeholder="Fruits" /></template>