Listbox
A component for selecting a single or multiple items from a list.
Anatomy
To set up the Listbox correctly, you'll need to understand its anatomy and how we name its parts.
Each part includes a
data-part
attribute to help identify them in the DOM.
Examples
Basic
Here's a basic example of the Listbox component.
import { Listbox, createListCollection } from '@ark-ui/react/listbox'
export const Basic = () => {
const collection = createListCollection({ items: ['React', 'Solid', 'Vue', 'Svelte'] })
return (
<Listbox.Root collection={collection}>
<Listbox.Label>Select your Framework</Listbox.Label>
<Listbox.Content>
{collection.items.map((item) => (
<Listbox.Item key={item} item={item}>
<Listbox.ItemText>{item}</Listbox.ItemText>
<Listbox.ItemIndicator />
</Listbox.Item>
))}
</Listbox.Content>
</Listbox.Root>
)
}
import { Listbox, createListCollection } from '@ark-ui/solid/listbox'
import { Index } from 'solid-js'
export const Basic = () => {
const collection = createListCollection({ items: ['React', 'Solid', 'Vue', 'Svelte'] })
return (
<Listbox.Root collection={collection}>
<Listbox.Label>Select your Framework</Listbox.Label>
<Listbox.Content>
<Index each={collection.items}>
{(item) => (
<Listbox.Item item={item()}>
<Listbox.ItemText>{item()}</Listbox.ItemText>
<Listbox.ItemIndicator />
</Listbox.Item>
)}
</Index>
</Listbox.Content>
</Listbox.Root>
)
}
<script setup lang="ts">
import { Listbox, createListCollection } from '@ark-ui/vue/listbox'
const collection = createListCollection({
items: ['React', 'Solid', 'Vue', 'Svelte'],
})
</script>
<template>
<Listbox.Root :collection="collection">
<Listbox.Label>Framework</Listbox.Label>
<Listbox.Content>
<Listbox.Item v-for="item in collection.items" :key="item" :item="item">
<Listbox.ItemText>{{ item }}</Listbox.ItemText>
<Listbox.ItemIndicator />
</Listbox.Item>
</Listbox.Content>
</Listbox.Root>
</template>
<script lang="ts">
import { Listbox, createListCollection } from '@ark-ui/svelte/listbox'
const collection = createListCollection({ items: ['React', 'Solid', 'Vue', 'Svelte'] })
</script>
<Listbox.Root {collection}>
<Listbox.Label>Select your Framework</Listbox.Label>
<Listbox.Content>
{#each collection.items as item}
<Listbox.Item {item}>
<Listbox.ItemText>{item}</Listbox.ItemText>
<Listbox.ItemIndicator />
</Listbox.Item>
{/each}
</Listbox.Content>
</Listbox.Root>
Controlled
The Listbox component can be controlled by using the value
and onValueChange
props. This allows you to manage the
selected value externally.
import { Listbox, createListCollection } from '@ark-ui/react/listbox'
import { useState } from 'react'
export const Controlled = () => {
const collection = createListCollection({ items: ['React', 'Solid', 'Vue', 'Svelte'] })
const [value, setValue] = useState(['React'])
return (
<Listbox.Root value={value} onValueChange={(e) => setValue(e.value)} collection={collection}>
<Listbox.Label>Select your Framework</Listbox.Label>
<Listbox.Content>
{collection.items.map((item) => (
<Listbox.Item key={item} item={item}>
<Listbox.ItemText>{item}</Listbox.ItemText>
<Listbox.ItemIndicator />
</Listbox.Item>
))}
</Listbox.Content>
</Listbox.Root>
)
}
import { Listbox, createListCollection } from '@ark-ui/solid/listbox'
import { Index, createSignal } from 'solid-js'
export const Controlled = () => {
const collection = createListCollection({ items: ['React', 'Solid', 'Vue', 'Svelte'] })
const [value, setValue] = createSignal(['React'])
return (
<Listbox.Root value={value()} onValueChange={(e) => setValue(e.value)} collection={collection}>
<Listbox.Label>Select your Framework</Listbox.Label>
<Listbox.Content>
<Index each={collection.items}>
{(item) => (
<Listbox.Item item={item()}>
<Listbox.ItemText>{item()}</Listbox.ItemText>
<Listbox.ItemIndicator />
</Listbox.Item>
)}
</Index>
</Listbox.Content>
</Listbox.Root>
)
}
<script setup lang="ts">
import { Listbox, createListCollection } from '@ark-ui/vue/listbox'
import { ref } from 'vue'
const collection = createListCollection({
items: ['React', 'Solid', 'Vue', 'Svelte'],
})
const value = ref(['React'])
</script>
<template>
<Listbox.Root :collection="collection" v-model="value">
<Listbox.Label>Framework</Listbox.Label>
<Listbox.Content>
<Listbox.Item v-for="item in collection.items" :key="item" :item="item">
<Listbox.ItemText>{{ item }}</Listbox.ItemText>
<Listbox.ItemIndicator />
</Listbox.Item>
</Listbox.Content>
</Listbox.Root>
</template>
<script lang="ts">
import { Listbox, createListCollection } from '@ark-ui/svelte/listbox'
const collection = createListCollection({
items: ['React', 'Solid', 'Vue', 'Svelte'],
})
let value = $state(['React'])
</script>
<Listbox.Root bind:value {collection}>
<Listbox.Label>Select your Framework</Listbox.Label>
<Listbox.Content>
{#each collection.items as item (item)}
<Listbox.Item {item}>
<Listbox.ItemText>{item}</Listbox.ItemText>
<Listbox.ItemIndicator />
</Listbox.Item>
{/each}
</Listbox.Content>
</Listbox.Root>
Disabled Item
Listbox items can be disabled using the disabled
prop on the collection item.
import { Listbox, createListCollection } from '@ark-ui/react/listbox'
export const Disabled = () => {
const collection = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Svelte', value: 'svelte', disabled: true },
{ label: 'Vue', value: 'vue' },
],
})
return (
<Listbox.Root collection={collection}>
<Listbox.Label>Select your Framework</Listbox.Label>
<Listbox.Content>
{collection.items.map((item) => (
<Listbox.Item key={item.value} item={item}>
<Listbox.ItemText>{item.label}</Listbox.ItemText>
<Listbox.ItemIndicator />
</Listbox.Item>
))}
</Listbox.Content>
</Listbox.Root>
)
}
import { Listbox, createListCollection } from '@ark-ui/solid/listbox'
import { Index } from 'solid-js'
export const Disabled = () => {
const collection = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Svelte', value: 'svelte', disabled: true },
{ label: 'Vue', value: 'vue' },
],
})
return (
<Listbox.Root collection={collection}>
<Listbox.Label>Select your Framework</Listbox.Label>
<Listbox.Content>
<Index each={collection.items}>
{(item) => (
<Listbox.Item item={item()}>
<Listbox.ItemText>{item().label}</Listbox.ItemText>
<Listbox.ItemIndicator />
</Listbox.Item>
)}
</Index>
</Listbox.Content>
</Listbox.Root>
)
}
<script setup lang="ts">
import { Listbox, createListCollection } from '@ark-ui/vue/listbox'
const collection = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Svelte', value: 'svelte', disabled: true },
{ label: 'Vue', value: 'vue' },
],
})
</script>
<template>
<Listbox.Root :collection="collection">
<Listbox.Label>Select your Framework</Listbox.Label>
<Listbox.Content>
<Listbox.Item v-for="item in collection.items" :key="item.value" :item="item">
<Listbox.ItemText>{{ item.label }}</Listbox.ItemText>
<Listbox.ItemIndicator />
</Listbox.Item>
</Listbox.Content>
</Listbox.Root>
</template>
<script lang="ts">
import { Listbox, createListCollection } from '@ark-ui/svelte/listbox'
const collection = createListCollection({
items: [
{ label: 'React', value: 'react' },
{ label: 'Solid', value: 'solid' },
{ label: 'Svelte', value: 'svelte', disabled: true },
{ label: 'Vue', value: 'vue' },
],
})
</script>
<Listbox.Root {collection}>
<Listbox.Label>Select your Framework</Listbox.Label>
<Listbox.Content>
{#each collection.items as item (item.value)}
<Listbox.Item {item}>
<Listbox.ItemText>{item.label}</Listbox.ItemText>
<Listbox.ItemIndicator />
</Listbox.Item>
{/each}
</Listbox.Content>
</Listbox.Root>
You can also use the
isItemDisabled
within thecreateListCollection
to disable items based on a condition.
Multiple Selection
You can set the selectionMode
property as multiple
to allow the user to select multiple items at a time.
import { Listbox, createListCollection } from '@ark-ui/react/listbox'
export const Multiple = () => {
const collection = createListCollection({ items: ['React', 'Solid', 'Vue', 'Svelte'] })
return (
<Listbox.Root collection={collection} selectionMode="multiple">
<Listbox.Label>Select your Framework</Listbox.Label>
<Listbox.Content>
{collection.items.map((item) => (
<Listbox.Item key={item} item={item}>
<Listbox.ItemText>{item}</Listbox.ItemText>
<Listbox.ItemIndicator />
</Listbox.Item>
))}
</Listbox.Content>
</Listbox.Root>
)
}
import { Listbox, createListCollection } from '@ark-ui/solid/listbox'
import { Index } from 'solid-js'
export const Multiple = () => {
const collection = createListCollection({ items: ['React', 'Solid', 'Vue', 'Svelte'] })
return (
<Listbox.Root collection={collection} selectionMode="multiple">
<Listbox.Label>Select your Framework</Listbox.Label>
<Listbox.Content>
<Index each={collection.items}>
{(item) => (
<Listbox.Item item={item()}>
<Listbox.ItemText>{item()}</Listbox.ItemText>
<Listbox.ItemIndicator />
</Listbox.Item>
)}
</Index>
</Listbox.Content>
</Listbox.Root>
)
}
<script setup lang="ts">
import { Listbox, createListCollection } from '@ark-ui/vue/listbox'
const collection = createListCollection({
items: ['React', 'Solid', 'Vue', 'Svelte'],
})
</script>
<template>
<Listbox.Root :collection="collection" selectionMode="multiple">
<Listbox.Label>Select your Framework</Listbox.Label>
<Listbox.Content>
<Listbox.Item v-for="item in collection.items" :key="item" :item="item">
<Listbox.ItemText>{{ item }}</Listbox.ItemText>
<Listbox.ItemIndicator />
</Listbox.Item>
</Listbox.Content>
</Listbox.Root>
</template>
<script lang="ts">
import { Listbox, createListCollection } from '@ark-ui/svelte/listbox'
const collection = createListCollection({ items: ['React', 'Solid', 'Vue', 'Svelte'] })
let value = $state(['React'])
</script>
<Listbox.Root {collection} bind:value selectionMode="multiple">
<Listbox.Label>Select your Frameworks (Multiple)</Listbox.Label>
<Listbox.ValueText placeholder="Select frameworks..." />
<Listbox.Content>
{#each collection.items as item}
<Listbox.Item {item}>
<Listbox.ItemText>{item}</Listbox.ItemText>
<Listbox.ItemIndicator />
</Listbox.Item>
{/each}
</Listbox.Content>
</Listbox.Root>
<div>
<p>Selected: {JSON.stringify(value)}</p>
</div>
Grouping
The Listbox component supports grouping items. You can use the groupBy
function to group items based on a specific
property.
import { Listbox, createListCollection } from '@ark-ui/react/listbox'
export const Group = () => {
const collection = createListCollection({
items: [
{ label: 'React', value: 'react', type: 'JS' },
{ label: 'Solid', value: 'solid', type: 'JS' },
{ label: 'Vue', value: 'vue', type: 'JS' },
{ label: 'Panda', value: 'panda', type: 'CSS' },
{ label: 'Tailwind', value: 'tailwind', type: 'CSS' },
],
groupBy: (item) => item.type,
})
return (
<Listbox.Root collection={collection}>
<Listbox.Label>Select your Frameworks</Listbox.Label>
<Listbox.Content>
{collection.group().map(([type, group]) => (
<Listbox.ItemGroup key={type}>
<Listbox.ItemGroupLabel>{type}</Listbox.ItemGroupLabel>
{group.map((item) => (
<Listbox.Item key={item.value} item={item}>
<Listbox.ItemText>{item.label}</Listbox.ItemText>
<Listbox.ItemIndicator />
</Listbox.Item>
))}
</Listbox.ItemGroup>
))}
</Listbox.Content>
</Listbox.Root>
)
}
import { Listbox, createListCollection } from '@ark-ui/solid/listbox'
import { For } from 'solid-js'
export const Group = () => {
const collection = createListCollection({
items: [
{ label: 'React', value: 'react', type: 'JS' },
{ label: 'Solid', value: 'solid', type: 'JS' },
{ label: 'Vue', value: 'vue', type: 'JS' },
{ label: 'Panda', value: 'panda', type: 'CSS' },
{ label: 'Tailwind', value: 'tailwind', type: 'CSS' },
],
groupBy: (item) => item.type,
})
return (
<Listbox.Root collection={collection}>
<Listbox.Label>Select your Frameworks</Listbox.Label>
<Listbox.Content>
<For each={collection.group()}>
{([type, group]) => (
<Listbox.ItemGroup>
<Listbox.ItemGroupLabel>{type}</Listbox.ItemGroupLabel>
<For each={group}>
{(item) => (
<Listbox.Item item={item}>
<Listbox.ItemText>{item.label}</Listbox.ItemText>
<Listbox.ItemIndicator />
</Listbox.Item>
)}
</For>
</Listbox.ItemGroup>
)}
</For>
</Listbox.Content>
</Listbox.Root>
)
}
<script setup lang="ts">
import { Listbox, createListCollection } from '@ark-ui/vue/listbox'
const collection = createListCollection({
items: [
{ label: 'React', value: 'react', type: 'JS' },
{ label: 'Solid', value: 'solid', type: 'JS' },
{ label: 'Vue', value: 'vue', type: 'JS' },
{ label: 'Panda', value: 'panda', type: 'CSS' },
{ label: 'Tailwind', value: 'tailwind', type: 'CSS' },
],
groupBy: (item) => item.type,
})
</script>
<template>
<Listbox.Root :collection="collection">
<Listbox.Label>Select your Frameworks</Listbox.Label>
<Listbox.Content>
<Listbox.ItemGroup v-for="[type, group] in collection.group()" :key="type">
<Listbox.ItemGroupLabel>{{ type }}</Listbox.ItemGroupLabel>
<Listbox.Item v-for="item in group" :key="item.value" :item="item">
<Listbox.ItemText>{{ item.label }}</Listbox.ItemText>
<Listbox.ItemIndicator />
</Listbox.Item>
</Listbox.ItemGroup>
</Listbox.Content>
</Listbox.Root>
</template>
<script lang="ts">
import { Listbox, createListCollection } from '@ark-ui/svelte/listbox'
const collection = createListCollection({
items: [
{ label: 'React', value: 'react', type: 'JS' },
{ label: 'Solid', value: 'solid', type: 'JS' },
{ label: 'Vue', value: 'vue', type: 'JS' },
{ label: 'Svelte', value: 'svelte', type: 'JS' },
{ label: 'Panda', value: 'panda', type: 'CSS' },
{ label: 'Tailwind', value: 'tailwind', type: 'CSS' },
],
groupBy: (item) => item.type,
})
</script>
<Listbox.Root {collection}>
<Listbox.Label>Select your Frameworks</Listbox.Label>
<Listbox.Content>
{#each collection.group() as [type, group]}
<Listbox.ItemGroup>
<Listbox.ItemGroupLabel>{type}</Listbox.ItemGroupLabel>
{#each group as item}
<Listbox.Item {item}>
<Listbox.ItemText>{item.label}</Listbox.ItemText>
<Listbox.ItemIndicator />
</Listbox.Item>
{/each}
</Listbox.ItemGroup>
{/each}
</Listbox.Content>
</Listbox.Root>
Guides
Type-Safety
The Listbox.RootComponent
type enables you to create closed, strongly typed wrapper components that maintain full type
safety for collection items.
This is particularly useful when building reusable listbox components with custom props and consistent styling.
import { Listbox as ArkListbox, type CollectionItem } from '@ark-ui/react/listbox'
import { createListCollection } from '@ark-ui/react/collection'
interface ListboxProps<T extends CollectionItem> extends ArkListbox.RootProps<T> {}
const Listbox: ArkListbox.RootComponent = (props) => {
return <ArkListbox.Root {...props}>{/* ... */}</ArkListbox.Root>
}
Then, you can use the Listbox
component as follows:
const App = () => {
const collection = createListCollection({
initialItems: [
{ label: 'React', value: 'react' },
{ label: 'Vue', value: 'vue' },
{ label: 'Svelte', value: 'svelte' },
],
})
return (
<Listbox
collection={collection}
onValueChange={(e) => {
// this will be strongly typed Array<{ label: string, value: string }>
console.log(e.items)
}}
>
{/* ... */}
</Listbox>
)
}
API Reference
Root
Prop | Default | Type |
---|---|---|
collection | ListCollection<T> The collection of items | |
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
defaultHighlightedValue | string The initial value of the highlighted item when opened. Use when you don't need to control the highlighted value of the listbox. | |
defaultValue | [] | string[] The initial default value of the listbox when rendered. Use when you don't need to control the value of the listbox. |
deselectable | boolean Whether to disallow empty selection | |
disabled | boolean Whether the listbox is disabled | |
disallowSelectAll | boolean Whether to disallow selecting all items when `meta+a` is pressed | |
highlightedValue | string The controlled key of the highlighted item | |
id | string The unique identifier of the machine. | |
ids | Partial<{
root: string
content: string
label: string
item: (id: string | number) => string
itemGroup: (id: string | number) => string
itemGroupLabel: (id: string | number) => string
}> The ids of the elements in the listbox. Useful for composition. | |
loopFocus | false | boolean Whether to loop the keyboard navigation through the options |
onHighlightChange | (details: HighlightChangeDetails<T>) => void The callback fired when the highlighted item changes. | |
onSelect | (details: SelectionDetails) => void Function called when an item is selected | |
onValueChange | (details: ValueChangeDetails<T>) => void The callback fired when the selected item changes. | |
orientation | 'vertical' | 'horizontal' | 'vertical' The orientation of the listbox. |
scrollToIndexFn | (details: ScrollToIndexDetails) => void Function to scroll to a specific index | |
selectionMode | 'single' | SelectionMode How multiple selection should behave in the listbox. - `single`: The user can select a single item. - `multiple`: The user can select multiple items without using modifier keys. - `extended`: The user can select multiple items by using modifier keys. |
selectOnHighlight | boolean Whether to select the item when it is highlighted | |
typeahead | boolean Whether to enable typeahead on the listbox | |
value | string[] The controlled keys of the selected items |
Data Attribute | Value |
---|---|
[data-scope] | listbox |
[data-part] | root |
[data-orientation] | The orientation of the listbox |
[data-disabled] | Present when disabled |
Content
Prop | Default | Type |
---|---|---|
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
CSS Variable | Description |
---|---|
--column-count | The column count value for the Content |
Data Attribute | Value |
---|---|
[data-scope] | listbox |
[data-part] | content |
[data-activedescendant] | The id the active descendant of the content |
[data-orientation] | The orientation of the content |
[data-layout] | |
[data-empty] | Present when the content is empty |
Empty
Prop | Default | Type |
---|---|---|
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Input
Prop | Default | Type |
---|---|---|
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
autoHighlight | false | boolean Whether to automatically highlight the item when typing |
Data Attribute | Value |
---|---|
[data-scope] | listbox |
[data-part] | input |
[data-disabled] | Present when disabled |
ItemGroupLabel
Prop | Default | Type |
---|---|---|
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
ItemGroup
Prop | Default | Type |
---|---|---|
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Data Attribute | Value |
---|---|
[data-scope] | listbox |
[data-part] | item-group |
[data-disabled] | Present when disabled |
[data-orientation] | The orientation of the item |
[data-empty] | Present when the content is empty |
ItemIndicator
Prop | Default | Type |
---|---|---|
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Data Attribute | Value |
---|---|
[data-scope] | listbox |
[data-part] | item-indicator |
[data-state] | "checked" | "unchecked" |
Item
Prop | Default | Type |
---|---|---|
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
highlightOnHover | boolean Whether to highlight the item on hover | |
item | any The item to render |
Data Attribute | Value |
---|---|
[data-scope] | listbox |
[data-part] | item |
[data-value] | The value of the item |
[data-selected] | Present when selected |
[data-layout] | |
[data-state] | "checked" | "unchecked" |
[data-orientation] | The orientation of the item |
[data-highlighted] | Present when highlighted |
[data-disabled] | Present when disabled |
ItemText
Prop | Default | Type |
---|---|---|
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Data Attribute | Value |
---|---|
[data-scope] | listbox |
[data-part] | item-text |
[data-state] | "checked" | "unchecked" |
[data-disabled] | Present when disabled |
[data-highlighted] | Present when highlighted |
Label
Prop | Default | Type |
---|---|---|
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Data Attribute | Value |
---|---|
[data-scope] | listbox |
[data-part] | label |
[data-disabled] | Present when disabled |
RootProvider
Prop | Default | Type |
---|---|---|
value | UseListboxReturn<T> | |
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
ValueText
Prop | Default | Type |
---|---|---|
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
placeholder | string Text to display when no value is listboxed. |
Data Attribute | Value |
---|---|
[data-scope] | listbox |
[data-part] | value-text |
[data-disabled] | Present when disabled |