Components

Listbox

Standalone selectable list with single, multiple, filtered, and virtualized modes.

Listbox displays a persistent list for single or multiple selection. It supports grouped rows, typeahead, range selection, filtering, object values, native forms, and virtualization.

Use Select for a popup or Combobox for text input with suggestions.

Anatomy

  • #option: Custom content for every selectable row.
  • #group-label: Custom content for rows with type: "label".
  • #empty: Content shown when filtering leaves no selectable options.

The component owns role="listbox", option roles, selection/highlight state, hidden form inputs, and keyboard behavior. Style those generated parts with the matching ui keys.

Usage

Framework

VueProgressive framework.
NuxtFull-stack Vue framework.
ViteFrontend build tool.
VitestTest runner.

Selected: vue

Filter and select multiple

Use Arrow keys and Space, or click rows.

VueProgressive framework.
NuxtFull-stack Vue framework.
ViteFrontend build tool.
VitestTest runner.

Selected: vue

Examples

Multiple selection

Set multiple and bind an array. Ctrl+A or Cmd+A selects every enabled visible option; Shift+Arrow extends a keyboard range.

<template>
  <Listbox
    v-model="permissions"
    multiple
    :options="[
      { value: 'read', label: 'Read' },
      { value: 'write', label: 'Write' },
      { value: 'deploy', label: 'Deploy' },
    ]"
    aria-label="Permissions"
  />
</template>

Group labels and separators

Rows with type: "label" or type: "separator" remain non-selectable and are skipped by keyboard navigation.

<template>
  <Listbox
    v-model="tool"
    :options="[
      { type: 'label', label: 'Frameworks' },
      { value: 'vue', label: 'Vue' },
      { value: 'nuxt', label: 'Nuxt' },
      { type: 'separator' },
      { type: 'label', label: 'Tooling' },
      { value: 'vite', label: 'Vite' },
    ]"
    aria-label="Tool"
  />
</template>

Filtering

Set filterable to render a search input above the list. Bind v-model:search for external state or use filter for custom matching.

<script setup lang="ts">
import { ref } from "vue";

const search = ref("");
</script>

<template>
  <Listbox
    v-model="framework"
    v-model:search="search"
    filterable
    :options="options"
    filter-placeholder="Filter frameworks"
    aria-label="Framework"
  />
</template>

Large data sets

Set virtualize to render a bounded window with VueUse. Provide a stable row height; group and separator rows should use the same height in virtual mode.

<template>
  <Listbox
    v-model="result"
    virtualize
    :virtual-height="240"
    :virtual-item-height="40"
    :overscan="6"
    :options="results"
    aria-label="Search results"
  />
</template>

Object values

Use by to compare objects and serializeValue to control submitted form values. The model keeps the original object.

<template>
  <Listbox
    v-model="assignee"
    name="assignee"
    by="id"
    :serialize-value="user => String(user.id)"
    :options="users.map(user => ({ value: user, label: user.name }))"
    aria-label="Assignee"
  />
</template>

Field integration

Inside Field, Listbox inherits id, name, required/disabled/invalid state, and description/error IDs. Required invalid state appears after blur or an invalid submit, not on initial render.

<template>
  <Field label="Environment" name="environment" required>
    <Listbox v-model="environment" :options="environments" />
  </Field>
</template>

Cancel a change

Change events fire before state updates. Call details.cancel() to keep the current value, search, or highlight.

<template>
  <Listbox
    v-model="role"
    :options="roles"
    @value-change="(value, details) => value === 'owner' && details.cancel()"
  />
</template>

Virtualized focus

Initial focus scrolls a distant selected option into view. Manual scrolling remains under user control. Listbox removes aria-activedescendant while its active virtual row is unmounted.

Native Form Reset

A native form reset restores the initial model and clears interaction state. Calling preventDefault() on the reset event preserves the current value. Reset updates v-model without emitting value-change.

API Reference

Models

ModelTypeDefaultDescription
v-modelListboxValue | ListboxValue[]""Selected value or values.
v-model:searchstring""Filter input value.

Props

PropTypeDefaultDescription
optionsListboxOption[]requiredRows rendered by the listbox.
valueKeystring-Object key used as value.
labelKeystring"label"Object key used as label.
descriptionKeystring"description"Object key used as description.
disabledKeystring"disabled"Object key used as disabled state.
multiplebooleanfalseEnables multiple selection.
nullableValuestring""Empty value used by single selection and clear().
bystring | (a, b) => booleanObject.isValue comparator, including object identity by key.
serializeValue(value) => stringJSON/string conversionSerializes native form values and keys.
idstringfield/generated idHidden control id.
namestringfield nameNative form field name.
requiredbooleanfalseRequires at least one selected option.
disabledbooleanfalseDisables focus and selection.
readOnlybooleanfalseAllows navigation without value changes.
invalidbooleanfalseForces invalid styling and ARIA state.
autoFocusbooleanfalseFocuses the filter when present, otherwise the listbox, after mount.
loopbooleantrueWraps keyboard navigation.
orientation"vertical" | "horizontal""vertical"ARIA orientation and arrow-key axis.
dir"ltr" | "rtl""ltr"Horizontal keyboard direction.
selectionBehavior"toggle" | "replace""toggle"Multiple-selection behavior without modifier keys.
filterablebooleanfalseRenders the filter input.
filter(option, search) => booleanlabel contains searchCustom option filter.
filterPlaceholderstring"Filter options"Filter input placeholder.
emptyLabelstring"No options found."Default empty content.
highlightOnHoverbooleantruePointer hover changes the active option.
virtualizebooleanfalseEnables virtual rendering.
virtualItemHeightnumber40Virtual row height in px.
virtualHeightnumber240Virtual viewport height in px.
overscannumber5Extra virtual rows rendered outside the viewport.
ariaLabelstring-Accessible label when no visible label exists.
ariaLabelledbystring-Accessible label element id.
ariaDescribedbystringfield IDsAccessible description/error ids.
uiListboxUi-Classes for structural parts.

Emits

EventPayloadDescription
value-change(value, details)Fires before selection changes. Cancelable.
search-change(search, details)Fires before filter text changes. Cancelable.
highlight-change(option, details)Fires before the active option changes. Cancelable.
entry-focus(option, details)Fires when focus enters the listbox and before initial highlight. Cancelable.
leave(option, details)Fires when the pointer leaves the listbox. Cancelable.

Slots

SlotPropsDescription
option or option slotoption, value, label, description, isSelected, isHighlighted, isDisabled, selectSelectable row content.
group-labeloption, labelGroup label content.
empty-Empty filtered state.

Exposed Methods

MethodDescription
focus()Focuses the listbox.
clear(event?)Clears selection through the cancelable value-change path.

Option Shape

KeyTypeDescription
type"item" | "label" | "separator"Row type. Omit for selectable items.
valuestring | number | objectSelected model value.
labelstringVisible label and default filter/typeahead text.
descriptionstringOptional supporting text.
disabledbooleanDisables the row.
slotstringNamed slot used instead of #option.

UI Options

KeyDescription
rootRoot wrapper.
hiddenInputNative form input(s).
filterOptional search input.
contentFocusable listbox.
virtualWrapperVirtual rows wrapper.
emptyEmpty state.
groupLabelGroup label row.
separatorSeparator row.
optionSelectable option row.
indicatorDefault selected indicator.
optionTextLabel/description wrapper.
optionLabelOption label.
optionDescriptionOption description.

Styling Hooks

UI keyCSS classData attrs
rootakaza-listboxdata-akaza-state, data-akaza-orientation, data-akaza-disabled, data-akaza-readonly, data-akaza-invalid, data-akaza-focused, data-akaza-filled
hiddenInputakaza-listbox-hidden-inputnative validity attrs
filterakaza-listbox-filternative disabled/read-only attrs
contentakaza-listbox-contentlistbox ARIA attrs, virtual height style
virtualWrapperakaza-listbox-virtual-wrappervirtual transform/height style
emptyakaza-listbox-empty-
groupLabelakaza-listbox-group-labelrole="presentation"
separatorakaza-listbox-separatorrole="presentation", aria-hidden="true"
optionakaza-listbox-optiondata-akaza-state, data-akaza-highlighted, data-akaza-disabled, aria-selected, aria-disabled
indicatorakaza-listbox-indicator-
optionTextakaza-listbox-option-text-
optionLabelakaza-listbox-option-label-
optionDescriptionakaza-listbox-option-description-

Plain class applies to the root wrapper. Use ui.content and ui.option for the list and rows.

Keyboard

KeyBehavior
ArrowDown / ArrowUpMove through a vertical list.
ArrowRight / ArrowLeftMove through a horizontal list, respecting dir.
Home / EndMove to first/last enabled option.
Enter / SpaceSelect or toggle the active option.
Shift + ArrowExtend a multiple-selection range.
Ctrl+A / Cmd+ASelect all enabled visible options in multiple mode.
printable charactersTypeahead to a matching label.
filter EscapeClear non-empty filter text.