Listbox

ARIAGitHubnpm
A control that allows the user to toggle between checked and not checked.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<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

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.

PropDefaultType
as
"div"
AsTag | Component

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

asChild
boolean

Change 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 ConfigProvider or assumes LTR (left-to-right) reading mode.

disabled
boolean

When true, prevents the user from interacting with listbox

highlightOnHover
boolean

When true, hover over item will trigger highlight

modelValue
AcceptableValue | AcceptableValue[]

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

multiple
boolean

Whether multiple options can be selected or not.

name
string

The 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
boolean

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.

EventPayload
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.

Slot (default)Payload
modelValue
T | T[]

Current active value

ListboxRoot.vue source

Data attribute Value
[data-disabled]
Present when disabled

Filter

Input element to perform filtering.

PropDefaultType
as
"input"
AsTag | Component

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

asChild
boolean

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

Read our Composition guide for more details.

autoFocus
boolean

Focus on element when mounted.

disabled
boolean

When true, prevents the user from interacting with item

modelValue
string

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

EventPayload
update:modelValue
string
Slot (default)Payload
modelValue
string

Current input values

ListboxFilter.vue source

Data attribute Value
[data-disabled]
Present when disabled

Content

Contains all the listbox group and items.

PropDefaultType
as
"div"
AsTag | Component

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

asChild
boolean

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

Read our Composition guide for more details.

ListboxContent.vue source

Item

The item component.

PropDefaultType
as
"div"
AsTag | Component

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

asChild
boolean

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

Read our Composition guide for more details.

disabled
boolean

When true, prevents the user from interacting with the item.

value*
T

The value given as data when submitted with a name.

EventPayload
select
event: SelectEvent<T>

Event handler called when the selecting item.

It can be prevented by calling event.preventDefault.

Slot (default)Payload
selected
boolean

Whether the item is currently selected

ListboxItem.vue source

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.

PropDefaultType
as
"span"
AsTag | Component

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

asChild
boolean

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

Read our Composition guide for more details.

forceMount
boolean

Used to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries.

ListboxItemIndicator.vue source

Group

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

PropDefaultType
as
"div"
AsTag | Component

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

asChild
boolean

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

Read our Composition guide for more details.

ListboxGroup.vue source

GroupLabel

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

PropDefaultType
as
"div"
AsTag | Component

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

asChild
boolean

Change 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

ListboxGroupLabel.vue source

Virtualizer

Virtual container to achieve list virtualization.

PropDefaultType
estimateSize
number | ((index: number) => number)

Estimated size (in px) of each item

options*
T

List of items

overscan
number

Number 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

ListboxVirtualizer.vue source

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.