Listbox

GitHub
A control that allows the user to toggle between checked and not checked.
Fruits
Apple
Banana
Blueberry
Grapes
Pineapple
Vegetables
Aubergine
Broccoli
Carrot
Courgette
Leek
<script setup lang="ts">
import { AListboxContent, AListboxGroup, AListboxGroupLabel, AListboxItem, AListboxItemIndicator, AListboxRoot } from 'akar';

const fruits = ['Apple', 'Banana', 'Blueberry', 'Grapes', 'Pineapple'];
const vegetables = ['Aubergine', 'Broccoli', 'Carrot', 'Courgette', 'Leek'];
</script>

<template>
  <AListboxRoot class="inline-flex items-center relative">
    <AListboxContent class="rounded-md bg-background flex flex-col w-48 pointer-events-auto ring ring-ring shadow-lg">
      <AListboxGroup class="p-1 isolate">
        <AListboxGroupLabel class="text-xs color-text-highlighted font-semibold p-1.5 gap-1.5">
          Fruits
        </AListboxGroupLabel>
        <AListboxItem
          v-for="i in fruits"
          :key="i"
          :value="i"
          class="text-sm color-text p-1.5 outline-none flex gap-1.5 w-full cursor-pointer select-none transition-colors-280 items-start relative before:(rounded-md content-empty transition-colors-280 inset-px absolute -z-1) hover:not-[[data-disabled]]:color-text-highlighted hover:not-[[data-disabled]]:before:bg-background-elevated/50"
        >
          <span class="flex flex-1 flex-col min-w-0">
            {{ i }}
          </span>

          <span class="ms-auto inline-flex gap-1.5 items-center">
            <AListboxItemIndicator
              as-child
            >
              <i class="i-lucide:check shrink-0 size-5" />
            </AListboxItemIndicator>
          </span>
        </AListboxItem>
      </AListboxGroup>

      <AListboxGroup class="mt-2">
        <AListboxGroupLabel class="text-xs color-text-highlighted font-semibold p-1.5 gap-1.5">
          Vegetables
        </AListboxGroupLabel>
        <AListboxItem
          v-for="i in vegetables"
          :key="i"
          :value="i"
          class="text-sm color-text p-1.5 outline-none flex gap-1.5 w-full cursor-pointer select-none transition-colors-280 items-start relative before:(rounded-md content-empty transition-colors-280 inset-px absolute -z-1) hover:not-[[data-disabled]]:color-text-highlighted hover:not-[[data-disabled]]:before:bg-background-elevated/50"
        >
          <span class="flex flex-1 flex-col min-w-0">
            {{ i }}
          </span>

          <span class="ms-auto inline-flex gap-1.5 items-center">
            <AListboxItemIndicator
              as-child
            >
              <i class="i-lucide:check shrink-0 size-5" />
            </AListboxItemIndicator>
          </span>
        </AListboxItem>
      </AListboxGroup>
    </AListboxContent>
  </AListboxRoot>
</template>

Features

  • Can be controlled or uncontrolled.
  • Supports items, labels, groups of items.
  • Focus is fully managed.
  • Full keyboard navigation.
  • Supports Right to Left direction.
  • Different selection behavior.

Anatomy

Import all parts and piece them together.

<script setup>
import { AListboxContent, AListboxFilter, AListboxGroup, AListboxGroupLabel, AListboxItem, AListboxItemIndicator, AListboxRoot, AListboxVirtualizer } from 'akar';
</script>

<template>
  <AListboxRoot>
    <AListboxFilter />

    <AListboxContent>
      <AListboxItem>
        <AListboxItemIndicator />
      </AListboxItem>

      <!-- or with group -->
      <AListboxGroup>
        <AListboxGroupLabel />
        <AListboxItem>
          <AListboxItemIndicator />
        </AListboxItem>
      </AListboxGroup>

      <!-- or with virtual -->
      <AListboxVirtualizer>
        <AListboxItem>
          <AListboxItemIndicator />
        </AListboxItem>
      </AListboxVirtualizer>
    </AListboxContent>
  </AListboxRoot>
</template>

API Reference

Root

Contains all the parts of a listbox. An input will also render when used within a form to ensure events propagate correctly.

Props

Prop Default Type
as'div'APrimitiveAsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

asChildboolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

bystring | ((a: AcceptableValue, b: AcceptableValue) => boolean)

Use this to compare objects by a particular field, or pass your own comparison function for complete control over how objects are compared.

defaultValueAcceptableValue | AcceptableValue[]

The value of the listbox when initially rendered. Use when you do not need to control the state of the Listbox

dir'ltr' | 'rtl'

The reading direction of the listbox when applicable.
If omitted, inherits globally from ConfigProvider or assumes LTR (left-to-right) reading mode.

disabledboolean

When true, prevents the user from interacting with listbox

highlightOnHoverboolean

When true, hover over item will trigger highlight

modelValueAcceptableValue | AcceptableValue[]
multipleboolean

Whether multiple options can be selected or not.

namestring

The name of the field. Submitted with its owning form as part of a name/value pair.

orientation'vertical''horizontal' | 'vertical'

The orientation of the listbox.
Mainly so arrow navigation is done accordingly (left & right vs. up & down)

requiredboolean

When true, indicates that the user must set the value before the owning form can be submitted.

selectionBehavior'toggle''replace' | 'toggle'

How multiple selection should behave in the collection.

Emits

Event Type
entryFocus[event: CustomEvent<any>]

Event handler called when container is being focused. Can be prevented.

highlight[payload: { ref: HTMLElement; value: AcceptableValue; }]

Event handler when highlighted element changes.

leave[event: Event]

Event handler called when the mouse leave the container

update:modelValue[value: AcceptableValue]

Event handler called when the value changes.

Slots

Slot Type
modelValueAcceptableValue | AcceptableValue[]

The controlled value of the listbox. Can be binded with with v-model.

Data Attributes

Attribute Value
[data-disabled]Present when disabled

Filter

Input element to perform filtering.

Props

Prop Default Type
as'input'APrimitiveAsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

asChildboolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

autoFocusboolean

Focus on element when mounted.

disabledboolean

When true, prevents the user from interacting with listbox

modelValuestring

The controlled value of the listbox. Can be binded with with v-model.

Emits

Event Type
update:modelValue[string]

Event handler called when the value changes.

Slots

Slot Type
modelValuestring

The controlled value of the listbox. Can be binded with with v-model.

Data Attributes

Attribute Value
[data-disabled]Present when disabled

Content

Contains all the listbox group and items.

Props

Prop Default Type
as'div'APrimitiveAsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

asChildboolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

Item

The item component.

Props

Prop Default Type
as'div'APrimitiveAsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

asChildboolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

disabledboolean

When true, prevents the user from interacting with listbox

value*AcceptableValue

The value given as data when submitted with a name.

Emits

Event Type
select[event: SelectEvent<AcceptableValue>]

Event handler called when the selecting item.
It can be prevented by calling event.preventDefault.

Data Attributes

Attribute Value
[data-state]'checked' | 'unchecked'
[data-highlighted]Present when highlighted
[data-disabled]Present when disabled

ItemIndicator

Renders when the item is selected. You can style this element directly, or you can use it as a wrapper to put an icon into, or both.

Props

Prop Default Type
as'span'APrimitiveAsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

asChildboolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

Group

Used to group multiple items. use in conjunction with AListboxGroupLabel to ensure good accessibility via automatic labelling.

Props

Prop Default Type
as'div'APrimitiveAsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

asChildboolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

GroupLabel

Used to render the label of a group. It won't be focusable using arrow keys.

Props

Prop Default Type
as'div'APrimitiveAsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

asChildboolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

forstring

Virtualizer

Virtual container to achieve list virtualization.

Props

Prop Default Type
estimateSizenumber

Estimated size (in px) of each item

options*AcceptableValue[]

List of items

overscannumber

Number of items rendered outside the visible area

textContent((option: AcceptableValue) => string)

Text content for each item to achieve type-ahead feature

Slots

Slot Type
optionnull | string | number | bigint | Record<string, any>
virtualizerVirtualizer<HTMLElement, Element>
virtualItemVirtualItem

Examples

Binding objects as values

Unlike native HTML form controls which only allow you to provide strings as values, akar supports binding complex objects as well.

<script setup lang="ts">
import { AListboxContent, AListboxItem, AListboxRoot } from 'akar';
import { ref } from 'vue';

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
];
const selectedPeople = ref(people[0]);
</script>

<template>
  <AListboxRoot v-model="selectedPeople">
    <AListboxContent>
      <AListboxItem
        v-for="person in people"
        :key="person.id"
        :value="person"
        :disabled="person.unavailable"
      >
        {{ person.name }}
      </AListboxItem>
    </AListboxContent>
  </AListboxRoot>
</template>

Selecting multiple values

The AListbox component allows you to select multiple values. You can enable this by providing an array of values instead of a single value.

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

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
];
const selectedPeople = ref([people[0], people[1]]);
</script>

<template>
  <AListboxRoot
    v-model="selectedPeople"
    multiple
  >
    ...
  </AListboxRoot>
</template>

Custom filtering

<script setup lang="ts">
import { AListboxContent, AListboxFilter, AListboxItem, AListboxRoot, useFilter } from 'akar';
import { ref } from 'vue';

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
];
const selectedPeople = ref(people[0]);
const searchTerm = ref('');

const { startsWith } = useFilter({ sensitivity: 'base' });
const filteredPeople = computed(() => people.filter((p) => startsWith(p.name, searchTerm.value)));
</script>

<template>
  <AListboxRoot v-model="selectedPeople">
    <AListboxFilter v-model="searchTerm" />
    <AListboxContent>
      <AListboxItem
        v-for="person in filteredPeople"
        :key="person.id"
        :value="person"
      >
        {{ person.name }}
      </AListboxItem>
    </AListboxContent>
  </AListboxRoot>
</template>

Virtual List

Rendering a long list of item can slow down the app, thus using virtualization would significantly improve the performance.

See the virtualization guide for more general info on virtualization.

<script setup lang="ts">
import { AListboxContent, AListboxFilter, AListboxItem, AListboxRoot, AListboxVirtualizer } from 'akar';
import { ref } from 'vue';

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
  // and a lot more
];
</script>

<template>
  <AListboxRoot>
    <AListboxContent>
      <AListboxVirtualizer
        v-slot="{ option }"
        :options="people"
        :text-content="(opt) => opt.name"
      >
        <AListboxItem :value="option">
          {{ person.name }}
        </AListboxItem>
      </AListboxVirtualizer>
    </AListboxContent>
  </AListboxRoot>
</template>

Accessibility

Adheres to the AListbox WAI-ARIA design pattern.

Keyboard Interactions

Key Description
Enter

When highlight on AListboxItem, selects the focused item.

ArrowDown

When focus is on AListboxItem, moves focus to the next item.

ArrowUp

When focus is on AListboxItem, moves focus to the previous item.

Home

Moves focus and highlight to the first item.

End

Moves focus and highlight to the last item.

Ctrl/Cmd + A

Select all the items.