<script setup lang="ts">
import { ListboxContent, ListboxItem, ListboxRoot } from 'akar';
</script>
<template>
<div class="akar-demo akar-demo-Listbox">
<ListboxRoot class="color-green9 mx-auto p-1 border rounded-lg bg-white flex flex-col h-72 w-48 overflow-auto">
<ListboxContent>
<ListboxItem
v-for="i in 20"
:key="i"
:value="i"
class="color-green9 data-[highlighted]:outline-green9 focus:outline-green9 data-[state=checked]:bg-green9 text-sm px-2 py-1 rounded w-full select-none data-[state=checked]:color-white data-[highlighted]:outline-1 data-[highlighted]:outline data-[disabled]:opacity-50 focus:ring-0"
>
{{ i }}
</ListboxItem>
</ListboxContent>
</ListboxRoot>
</div>
</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.
Installation
Install the component from your command line.
pnpm add akar
yarn add akar
npm install akar
bun add akar
Anatomy
Import all parts and piece them together.
<script setup>
import { ListboxContent, ListboxFilter, ListboxGroup, ListboxGroupLabel, ListboxItem, ListboxItemIndicator, ListboxRoot, ListboxVirtualizer } from 'akar';
</script>
<template>
<ListboxRoot>
<ListboxFilter />
<ListboxContent>
<ListboxItem>
<ListboxItemIndicator />
</ListboxItem>
<!-- or with group -->
<ListboxGroup>
<ListboxGroupLabel />
<ListboxItem>
<ListboxItemIndicator />
</ListboxItem>
</ListboxGroup>
<!-- or with virtual -->
<ListboxVirtualizer>
<ListboxItem>
<ListboxItemIndicator />
</ListboxItem>
</ListboxVirtualizer>
</ListboxContent>
</ListboxRoot>
</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.
| Prop | Default | Type |
|---|---|---|
as | "div" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
by | string | ((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. | |
defaultValue | AcceptableValue | 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 | |
disabled | booleanWhen | |
highlightOnHover | booleanWhen | |
modelValue | AcceptableValue | AcceptableValue[]The controlled value of the listbox. Can be binded with | |
multiple | booleanWhether multiple options can be selected or not. | |
name | stringThe name of the field. Submitted with its owning form as part of a name/value pair. | |
orientation | "vertical" | "vertical" | "horizontal"The orientation of the listbox. Mainly so arrow navigation is done accordingly (left & right vs. up & down) |
required | booleanWhen | |
selectionBehavior | "toggle" | "replace" | "toggle"How multiple selection should behave in the collection. |
| Event | Payload |
|---|---|
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: EventEvent handler called when the mouse leave the container |
update:modelValue | value: AcceptableValueEvent handler called when the value changes. |
| Slot (default) | Payload |
|---|---|
modelValue | T | T[]Current active value |
| Data attribute | Value |
|---|---|
[data-disabled] | Present when disabled |
Filter
Input element to perform filtering.
| Prop | Default | Type |
|---|---|---|
as | "input" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
autoFocus | booleanFocus on element when mounted. | |
disabled | booleanWhen | |
modelValue | stringThe controlled value of the filter. Can be binded with v-model. |
| Event | Payload |
|---|---|
update:modelValue | string |
| Slot (default) | Payload |
|---|---|
modelValue | stringCurrent input values |
| Data attribute | Value |
|---|---|
[data-disabled] | Present when disabled |
Content
Contains all the listbox group and items.
| Prop | Default | Type |
|---|---|---|
as | "div" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange 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.
| Prop | Default | Type |
|---|---|---|
as | "div" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
disabled | booleanWhen | |
value* | TThe value given as data when submitted with a |
| Event | Payload |
|---|---|
select | event: SelectEvent<T>Event handler called when the selecting item. It can be prevented by calling |
| Slot (default) | Payload |
|---|---|
selected | booleanWhether the item is currently selected |
| Data 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.
| Prop | Default | Type |
|---|---|---|
as | "span" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
forceMount | booleanUsed to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. |
Group
Used to group multiple items. use in conjunction with ListboxGroupLabel to ensure good accessibility via automatic labelling.
| Prop | Default | Type |
|---|---|---|
as | "div" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange 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.
| Prop | Default | Type |
|---|---|---|
as | "div" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
for | string |
Virtualizer
Virtual container to achieve list virtualization.
| Prop | Default | Type |
|---|---|---|
estimateSize | number | ((index: number) => number)Estimated size (in px) of each item | |
options* | TList of items | |
overscan | numberNumber of items rendered outside the visible area | |
textContent | ((option: T) => string)Text content for each item to achieve type-ahead feature |
| Slot (default) | Payload |
|---|---|
option | T |
virtualizer | Virtualizer<HTMLElement, Element> |
virtualItem | VirtualItem |
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 { ListboxContent, ListboxFilter, ListboxItem, ListboxRoot } 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>
<ListboxRoot v-model="selectedPeople">
<ListboxContent>
<ListboxItem
v-for="person in people"
:key="person.id"
:value="person"
:disabled="person.unavailable"
>
{{ person.name }}
</ListboxItem>
</ListboxContent>
</ListboxRoot>
</template>
Selecting multiple values
The Listbox 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 { ListboxRoot } 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>
<ListboxRoot
v-model="selectedPeople"
multiple
>
...
</ListboxRoot>
</template>
Custom filtering
<script setup lang="ts">
import { ListboxContent, ListboxFilter, ListboxItem, ListboxRoot, 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>
<ListboxRoot v-model="selectedPeople">
<ListboxFilter v-model="searchTerm" />
<ListboxContent>
<ListboxItem
v-for="person in filteredPeople"
:key="person.id"
:value="person"
>
{{ person.name }}
</ListboxItem>
</ListboxContent>
</ListboxRoot>
</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 { ListboxContent, ListboxFilter, ListboxItem, ListboxRoot, ListboxVirtualizer } 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>
<ListboxRoot>
<ListboxContent>
<ListboxVirtualizer
v-slot="{ option }"
:options="people"
:text-content="(opt) => opt.name"
>
<ListboxItem :value="option">
{{ person.name }}
</ListboxItem>
</ListboxVirtualizer>
</ListboxContent>
</ListboxRoot>
</template>
Accessibility
Adheres to the Listbox WAI-ARIA design pattern.
Keyboard Interactions
| Key | Description |
|---|---|
| Enter | When highlight on ListboxItem, selects the focused item. |
| ArrowDown | When focus is on ListboxItem, moves focus to the next item. |
| ArrowUp | When focus is on ListboxItem, 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. |