Skip to content
v1.0.3

Combobox

A searchable select. Single value by default; multiple renders selected items as removable chips, creatable lets users add new values from the typed text, and loading + onInputChange support async option sets. Full keyboard nav (↑/↓, Enter, Esc).

The HTML behavior layer covers the common single-select case (filter + keyboard + select on declarative options). Multi-select chips, creatable, and async are React/Vue features.

bash
jlds add combobox

Usage

html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/jarooda/jlds@main/registry/css/index.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/jarooda/jlds@main/registry/css/combobox.css">
<!-- behavior layer: single-select filter + keyboard (needs util.js) -->
<script src="https://cdn.jsdelivr.net/gh/jarooda/jlds@main/registry/js/core.js" defer></script>
<script src="https://cdn.jsdelivr.net/gh/jarooda/jlds@main/registry/js/util.js" defer></script>
<script src="https://cdn.jsdelivr.net/gh/jarooda/jlds@main/registry/js/combobox.js" defer></script>

<div class="jl-combobox jl-combobox--md">
  <div class="jl-combobox__control">
    <span class="jl-combobox__field">
      <span class="jl-combobox__single jl-combobox__single--placeholder">Select a framework…</span>
      <input class="jl-combobox__input" role="combobox" aria-expanded="false" aria-autocomplete="list" />
    </span>
    <span class="jl-combobox__adorn">
      <span class="jl-combobox__btn jl-combobox__chevron" aria-hidden="true">
        <svg viewBox="0 0 16 16" fill="none"><path d="M4 6l4 4 4-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
      </span>
    </span>
  </div>
  <div class="jl-combobox__pop" role="listbox" hidden>
    <div class="jl-combobox__opt" role="option" data-value="react"><span class="jl-combobox__opt-label">React</span></div>
    <div class="jl-combobox__opt" role="option" data-value="vue"><span class="jl-combobox__opt-label">Vue</span></div>
    <div class="jl-combobox__opt" role="option" data-value="svelte"><span class="jl-combobox__opt-label">Svelte</span></div>
  </div>
</div>
vue
<script setup lang="ts">
import { ref } from "vue"
import { Combobox } from "@/components/ui/combobox"

const framework = ref<string | null>(null)
</script>

<template>
  <Combobox
    v-model="framework"
    :options="['React', 'Vue', 'Svelte', 'Angular', 'Solid']"
    placeholder="Select a framework…"
  />
</template>
tsx
import { useState } from "react"
import { Combobox } from "@/components/ui/combobox"

const [framework, setFramework] = useState<string | null>(null)

<Combobox
  value={framework}
  onChange={(v) => setFramework(v as string | null)}
  options={["React", "Vue", "Svelte", "Angular", "Solid"]}
  placeholder="Select a framework…"
/>

Multiple, creatable, clearable

multiple shows chips; creatable adds a “Create …” row; clearable adds an × to wipe the selection. (React/Vue.)

vue
<template>
  <Combobox v-model="tags" :options="opts" multiple creatable clearable placeholder="Add tags…" />
</template>
tsx
<Combobox value={tags} onChange={setTags} options={opts} multiple creatable clearable placeholder="Add tags…" />

Async options

Set loading and handle onInputChange (Vue: @input-change) to fetch options as the user types — local filtering is disabled while you drive the list.

tsx
<Combobox options={results} loading={pending} onInputChange={search} placeholder="Search users…" />

Props

PropTypeDefaultDescription
options(string | { value, label, icon?, group?, disabled? })[]The options
value / defaultValuestring | string[] | nullControlled / uncontrolled (v-model in Vue)
onChange(value) => voidNext value (Vue: update:modelValue)
multiplebooleanfalseMulti-select chips
creatablebooleanfalseAdd new values from typed text
loadingbooleanfalseSpinner + loadingMessage
onInputChange(query) => voidPer-keystroke; disables local filtering (Vue: @input-change)
clearablebooleanfalseShow an × to clear
size"sm" | "md" | "lg""md"Control height
disabled / invalidbooleanfalseStates
placeholder / emptyMessage / loadingMessagestringCopy

CSS classes (HTML)

ClassPurpose
.jl-combobox + --sm/md/lg / --open / --invalid / --disabledRoot + size + states
.jl-combobox__control / __field / __input / __singleThe input control
.jl-combobox__chip / __chip-xMulti-select chips
.jl-combobox__popThe options popup (start it hidden)
.jl-combobox__opt + [data-active] / [aria-selected]An option (set data-value)
.jl-combobox__opt-label / __opt-icon / __opt-checkOption parts
.jl-combobox__chevron / __spinnerChevron / loading spinner