Hesoyam

Combobox

Type to filter a known list, then pick one value. Use Select when the list is short and typing adds nothing.

Uncontrolled


        
1 <Combobox aria-label="Region" placeholder="Search regions">
2 <ComboboxInput />
3 <ComboboxList>
4 <ComboboxOption value="iad">Washington, D.C.</ComboboxOption>
5 <ComboboxOption value="fra">Frankfurt</ComboboxOption>
6 </ComboboxList>
7 </Combobox>

Controlled

v-model is the Vue path. value plus @change (or onChange) stay for Hesoyam-shaped call sites.

Selected: fra


        
1 <Combobox v-model="region" aria-label="Region" placeholder="Search regions">
2 <ComboboxInput />
3 <ComboboxList>
4 <ComboboxOption value="fra">Frankfurt</ComboboxOption>
5 </ComboboxList>
6 </Combobox>

Sizes

small is 24px, medium is 32px, large is 40px. default matches medium.


        
1 <Combobox size="small" aria-label="Region" placeholder="Search regions">
2 <ComboboxInput />
3 <ComboboxList>…</ComboboxList>
4 </Combobox>

Disabled and errored


        
1 <Combobox disabled aria-label="Region" placeholder="Search regions">
2 <ComboboxInput />
3 <ComboboxList>…</ComboboxList>
4 </Combobox>
5 <Combobox errored aria-label="Region" placeholder="Search regions">
6 <ComboboxInput />
7 <ComboboxList>…</ComboboxList>
8 </Combobox>

Clearable

clearable shows a clear control after a value is selected.


        
1 <Combobox v-model="value" clearable aria-label="Size" placeholder="Search sizes">
2 <ComboboxInput />
3 <ComboboxList>
4 <ComboboxOption value="one">one</ComboboxOption>
5 <ComboboxOption value="two">two</ComboboxOption>
6 </ComboboxList>
7 </Combobox>

Width

width sizes the input in pixels. maxWidth on the list lets long option text grow instead of wrapping to the input.


        
1 <Combobox :width="256" aria-label="Region" placeholder="Search regions">
2 <ComboboxInput />
3 <ComboboxList :max-width="420">
4 <ComboboxOption value="long">
5 Frankfurt — EU Central, lowest latency for this workspace.
6 </ComboboxOption>
7 </ComboboxList>
8 </Combobox>

Empty message

Override the default No items match “query” copy when the empty state needs a noun.


        
1 <Combobox aria-label="Token" placeholder="Search tokens" :width="256">
2 <ComboboxInput />
3 <ComboboxList empty-message="No tokens match this query." />
4 </Combobox>

Prefix and suffix


        
1 <ComboboxOption value="nuxt">
2 <template #prefix>
3 <Icon :icon="SourceCodeIcon" :size="14" />
4 </template>
5 Nuxt
6 </ComboboxOption>

With a label

Combobox has no label prop. Pair a Label with the same id, or set aria-label on the root.


        
1 <Label :html-for="id" value="Region" with-input />
2 <Combobox :id="id" aria-label="Region" placeholder="Search regions">
3 <ComboboxInput />
4 <ComboboxList>…</ComboboxList>
5 </Combobox>

Multi-line options

ignoreDefaultHeight drops the fixed 32px row so a name plus meta can stack. The list still scrolls.


        
1 <ComboboxOption ignore-default-height value="DATABASE_URL::prod">
2 <div class="flex flex-col py-2">
3 <span class="font-mono text-[13px]">DATABASE_URL</span>
4 <span class="text-[12px] text-[var(--ds-gray-900)]">Production</span>
5 </div>
6 </ComboboxOption>

Best practices

  • Combobox filters a known list (regions, frameworks, env names). A short fixed list is Select. More than one value is Multi Select. A free-form query is Search Input.
  • Visible label is a short Title Case noun. Placeholder is the hint — Search regions — not a repeat of the label and not a bare Search….
  • Empty copy should name the collection: No regions match “eu”. Set empty-message when the default is too generic.
  • Do not submit the surrounding form on Enter while the list is open. Arrow keys move, Enter selects, Escape closes.
  • errored only paints the border. Put the constraint in a sentence under the field: Select a region.
  • Prefix and suffix accept a slot or a prefix / suffix prop. Prefer the slot with <Icon>.