"use client"
import { Listbox, createListCollection } from "@chakra-ui/react"
export const ListboxBasic = () => {
return (
<Listbox.Root collection={frameworks} width="320px">
<Listbox.Label>Select framework</Listbox.Label>
<Listbox.Content>
{frameworks.items.map((framework) => (
<Listbox.Item item={framework} key={framework.value}>
<Listbox.ItemText>{framework.label}</Listbox.ItemText>
<Listbox.ItemIndicator />
</Listbox.Item>
))}
</Listbox.Content>
</Listbox.Root>
)
}
const frameworks = createListCollection({
items: [
{ label: "React.js", value: "react" },
{ label: "Vue.js", value: "vue" },
{ label: "Angular", value: "angular" },
{ label: "Svelte", value: "svelte" },
],
})
Anatomy
import { Listbox } from '@chakra-ui/react'<Listbox.Root>
<Listbox.Label />
<Listbox.Content>
<Listbox.Item>
<Listbox.ItemText />
<Listbox.ItemIndicator />
</Listbox.Item>
</Listbox.Content>
</Listbox.Root>To set up the listbox, use createListCollection to create a static collection,
or the useListCollection hook when the list needs to be filtered or mutated at
runtime.
Examples
Basic
Pass a collection to Listbox.Root and map over collection.items to render
each option.
"use client"
import { Listbox, createListCollection } from "@chakra-ui/react"
export const ListboxBasic = () => {
return (
<Listbox.Root collection={frameworks} width="320px">
<Listbox.Label>Select framework</Listbox.Label>
<Listbox.Content>
{frameworks.items.map((framework) => (
<Listbox.Item item={framework} key={framework.value}>
<Listbox.ItemText>{framework.label}</Listbox.ItemText>
<Listbox.ItemIndicator />
</Listbox.Item>
))}
</Listbox.Content>
</Listbox.Root>
)
}
const frameworks = createListCollection({
items: [
{ label: "React.js", value: "react" },
{ label: "Vue.js", value: "vue" },
{ label: "Angular", value: "angular" },
{ label: "Svelte", value: "svelte" },
],
})
Controlled
Control the listbox value externally using the value and onValueChange
props for custom state management.
Selected: []"use client"
import { Code, Listbox, Stack, createListCollection } from "@chakra-ui/react"
import { useState } from "react"
export const ListboxControlled = () => {
const [value, setValue] = useState<string[]>([])
return (
<Stack maxWidth="320px" width="full" gap="4">
<Listbox.Root
collection={frameworks}
value={value}
onValueChange={(details) => setValue(details.value)}
>
<Listbox.Label>Select framework</Listbox.Label>
<Listbox.Content>
{frameworks.items.map((framework) => (
<Listbox.Item item={framework} key={framework.value}>
<Listbox.ItemText>{framework.label}</Listbox.ItemText>
<Listbox.ItemIndicator />
</Listbox.Item>
))}
</Listbox.Content>
</Listbox.Root>
<Code alignSelf="flex-start">
Selected: {JSON.stringify(value, null, 2)}
</Code>
</Stack>
)
}
const frameworks = createListCollection({
items: [
{ label: "React.js", value: "react" },
{ label: "Vue.js", value: "vue" },
{ label: "Angular", value: "angular" },
{ label: "Svelte", value: "svelte" },
],
})
Multiple Selection
Set the selectionMode prop to "multiple" to let users select more than one
item, useful for scenarios like choosing tags, categories or preferences.
"use client"
import { Listbox, createListCollection } from "@chakra-ui/react"
export const ListboxMultiselect = () => {
return (
<Listbox.Root collection={frameworks} selectionMode="multiple" maxW="320px">
<Listbox.Label>Select frameworks (multiple)</Listbox.Label>
<Listbox.Content>
{frameworks.items.map((framework) => (
<Listbox.Item item={framework} key={framework.value}>
<Listbox.ItemText>{framework.label}</Listbox.ItemText>
<Listbox.ItemIndicator />
</Listbox.Item>
))}
</Listbox.Content>
</Listbox.Root>
)
}
const frameworks = createListCollection({
items: [
{ label: "React.js", value: "react" },
{ label: "Vue.js", value: "vue" },
{ label: "Angular", value: "angular" },
{ label: "Svelte", value: "svelte" },
{ label: "Next.js", value: "nextjs" },
{ label: "Nuxt.js", value: "nuxtjs" },
],
})
Grouped
Pass a groupBy function to the collection and render Listbox.ItemGroup to
organize related options under clear section headers.
"use client"
import { Listbox, createListCollection } from "@chakra-ui/react"
export const ListboxGrouped = () => {
return (
<Listbox.Root collection={collection} width="320px">
<Listbox.Label>Select media</Listbox.Label>
<Listbox.Content divideY="1px">
{collection.group().map(([category, items]) => (
<Listbox.ItemGroup key={category}>
<Listbox.ItemGroupLabel>{category}</Listbox.ItemGroupLabel>
{items.map((item) => (
<Listbox.Item item={item} key={item.value}>
<Listbox.ItemText>{item.label}</Listbox.ItemText>
<Listbox.ItemIndicator />
</Listbox.Item>
))}
</Listbox.ItemGroup>
))}
</Listbox.Content>
</Listbox.Root>
)
}
const collection = createListCollection({
items: [
{ label: "Naruto", value: "naruto", category: "Anime" },
{ label: "One Piece", value: "one-piece", category: "Anime" },
{ label: "Dragon Ball", value: "dragon-ball", category: "Anime" },
{
label: "The Shawshank Redemption",
value: "the-shawshank-redemption",
category: "Movies",
},
{ label: "The Godfather", value: "the-godfather", category: "Movies" },
{ label: "The Dark Knight", value: "the-dark-knight", category: "Movies" },
],
groupBy: (item) => item.category,
})
With Icon
Add icons to listbox items to provide visual context and improve recognition of different options.
"use client"
import { Box, Listbox, createListCollection } from "@chakra-ui/react"
import { LuAtom, LuGlobe, LuPalette, LuZap } from "react-icons/lu"
export const ListboxWithIcon = () => {
return (
<Listbox.Root collection={frameworks} maxW="320px">
<Listbox.Label>Select framework</Listbox.Label>
<Listbox.Content>
{frameworks.items.map((framework) => (
<Listbox.Item item={framework} key={framework.value}>
<Box display="flex" alignItems="center" gap="3" flex="1">
<Box color="fg.muted" flexShrink="0">
{framework.icon}
</Box>
<Listbox.ItemText>{framework.label}</Listbox.ItemText>
</Box>
<Listbox.ItemIndicator />
</Listbox.Item>
))}
</Listbox.Content>
</Listbox.Root>
)
}
const frameworks = createListCollection({
items: [
{ label: "React.js", value: "react", icon: <LuAtom size={16} /> },
{ label: "Vue.js", value: "vue", icon: <LuPalette size={16} /> },
{ label: "Angular", value: "angular", icon: <LuGlobe size={16} /> },
{ label: "Svelte", value: "svelte", icon: <LuZap size={16} /> },
],
})
With Description
Include additional descriptive text for each item to provide more context and help users make informed choices.
A JavaScript library for building user interfaces
The progressive JavaScript framework
Platform for building mobile and desktop web applications
Cybernetically enhanced web apps
The React framework for production
"use client"
import { Box, Listbox, Text, createListCollection } from "@chakra-ui/react"
export const ListboxWithDescription = () => {
return (
<Listbox.Root collection={frameworks} maxW="400px">
<Listbox.Label>Select framework</Listbox.Label>
<Listbox.Content>
{frameworks.items.map((framework) => (
<Listbox.Item item={framework} key={framework.value}>
<Box flex="1">
<Listbox.ItemText>{framework.label}</Listbox.ItemText>
<Text fontSize="xs" color="fg.muted" mt="1">
{framework.description}
</Text>
</Box>
<Listbox.ItemIndicator />
</Listbox.Item>
))}
</Listbox.Content>
</Listbox.Root>
)
}
const frameworks = createListCollection({
items: [
{
label: "React.js",
value: "react",
description: "A JavaScript library for building user interfaces",
},
{
label: "Vue.js",
value: "vue",
description: "The progressive JavaScript framework",
},
{
label: "Angular",
value: "angular",
description: "Platform for building mobile and desktop web applications",
},
{
label: "Svelte",
value: "svelte",
description: "Cybernetically enhanced web apps",
},
{
label: "Next.js",
value: "nextjs",
description: "The React framework for production",
},
],
})
With Input
Render Listbox.Input and filter the collection as the user types to make it
easy to find specific items in long lists. Use Listbox.Empty to render a
fallback when nothing matches.
"use client"
import { Input, Listbox, useFilter, useListCollection } from "@chakra-ui/react"
export const ListboxWithInput = () => {
const { contains } = useFilter({ sensitivity: "base" })
const { collection, filter } = useListCollection({
initialItems: [
{ label: "React.js", value: "react" },
{ label: "Vue.js", value: "vue" },
{ label: "Angular", value: "angular" },
{ label: "Svelte", value: "svelte" },
{ label: "Next.js", value: "nextjs" },
{ label: "Nuxt.js", value: "nuxtjs" },
{ label: "Remix", value: "remix" },
{ label: "Gatsby", value: "gatsby" },
{ label: "Ember.js", value: "ember" },
{ label: "Preact", value: "preact" },
],
filter: contains,
})
return (
<Listbox.Root maxW="320px" collection={collection}>
<Listbox.Label>Select Framework</Listbox.Label>
<Listbox.Input
as={Input}
placeholder="Type to filter frameworks..."
onChange={(e) => filter(e.target.value)}
/>
<Listbox.Content maxH="200px">
{collection.items.map((framework) => (
<Listbox.Item item={framework} key={framework.value}>
<Listbox.ItemText>{framework.label}</Listbox.ItemText>
<Listbox.ItemIndicator />
</Listbox.Item>
))}
<Listbox.Empty>No frameworks found</Listbox.Empty>
</Listbox.Content>
</Listbox.Root>
)
}
With Popover
Use the listbox within a popover to create dropdown-like selection menus that overlay other content without taking up permanent screen space.
"use client"
import {
Button,
Listbox,
Popover,
Portal,
useFilter,
useListCollection,
useListbox,
} from "@chakra-ui/react"
import { useRef, useState } from "react"
import { LuChevronDown } from "react-icons/lu"
export const ListboxWithPopover = () => {
const [inputValue, setInputValue] = useState("")
const [open, setOpen] = useState(false)
const { contains } = useFilter({ sensitivity: "base" })
const triggerRef = useRef<HTMLButtonElement | null>(null)
const { collection, filter } = useListCollection({
initialItems: [
{ label: "React.js", value: "react" },
{ label: "Vue.js", value: "vue" },
{ label: "Angular", value: "angular" },
{ label: "Svelte", value: "svelte" },
{ label: "Next.js", value: "nextjs" },
{ label: "Nuxt.js", value: "nuxtjs" },
],
filter: contains,
})
const listbox = useListbox({
collection,
onValueChange() {
setOpen(false)
setInputValueFn("")
triggerRef.current?.focus()
},
})
const setInputValueFn = (value: string) => {
setInputValue(value)
filter(value)
}
const selectedItem = listbox.selectedItems[0]
return (
<Popover.Root open={open} onOpenChange={(e) => setOpen(e.open)}>
<Popover.Trigger asChild>
<Button size="sm" ref={triggerRef} variant="outline">
{selectedItem ? selectedItem.label : "Select"} <LuChevronDown />
</Button>
</Popover.Trigger>
<Portal>
<Popover.Positioner>
<Popover.Content _closed={{ animation: "none" }}>
<Popover.Body p="0">
<Listbox.RootProvider value={listbox} gap="0" overflow="hidden">
<Listbox.Input
minH="10"
px="3"
roundedTop="l2"
bg="transparent"
outline="0"
value={inputValue}
onChange={(e) => setInputValueFn(e.currentTarget.value)}
/>
<Listbox.Content
borderWidth="0"
borderTopWidth="1px"
roundedTop="0"
gap="0"
>
{collection.items.map((framework) => (
<Listbox.Item item={framework} key={framework.value}>
<Listbox.ItemText>{framework.label}</Listbox.ItemText>
<Listbox.ItemIndicator />
</Listbox.Item>
))}
</Listbox.Content>
</Listbox.RootProvider>
</Popover.Body>
</Popover.Content>
</Popover.Positioner>
</Portal>
</Popover.Root>
)
}
Props
Root
| Prop | Default | Type |
|---|---|---|
collection * | ListCollection<T>The collection of items | |
defaultValue | '[]' | string[]The initial default value of the listbox when rendered. Use when you don't need to control the value of the listbox. |
loopFocus | false | booleanWhether to loop the keyboard navigation through the options |
orientation | '\'vertical\'' | 'horizontal' | 'vertical'The orientation of the listbox. |
selectionMode | '\'single\'' | SelectionModeHow 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. |
colorPalette | 'gray' | 'base' | 'gray' | 'zinc' | 'neutral' | 'stone' | 'red' | 'orange' | 'amber' | 'yellow' | 'lime' | 'green' | 'emerald' | 'teal' | 'cyan' | 'sky' | 'blue' | 'indigo' | 'violet' | 'purple' | 'fuchsia' | 'pink' | 'rose' | 'sidebar' | 'sidebar.accent' | 'interaction' | 'accent' | 'presence' | 'status' | 'slate'The color palette of the component |
variant | 'subtle' | 'subtle' | 'solid' | 'plain'The variant of the component |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
defaultHighlightedValue | stringThe initial value of the highlighted item when opened. Use when you don't need to control the highlighted value of the listbox. | |
deselectable | booleanWhether to disallow empty selection | |
disabled | booleanWhether the listbox is disabled | |
disallowSelectAll | booleanWhether to disallow selecting all items when `meta+a` is pressed | |
highlightedValue | stringThe controlled key of the highlighted item | |
id | stringThe 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. | |
onHighlightChange | (details: HighlightChangeDetails<T>) => voidThe callback fired when the highlighted item changes. | |
onSelect | (details: SelectionDetails) => voidFunction called when an item is selected | |
onValueChange | (details: ValueChangeDetails<T>) => voidThe callback fired when the selected item changes. | |
scrollToIndexFn | (details: ScrollToIndexDetails) => voidFunction to scroll to a specific index | |
selectOnHighlight | booleanWhether to select the item when it is highlighted | |
typeahead | booleanWhether to enable typeahead on the listbox | |
value | string[]The controlled keys of the selected items |
Label
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse 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 |
|---|---|---|
autoHighlight | false | booleanWhether to automatically highlight the item when typing |
keyboardPriority | '\'caret\'' | 'caret' | 'navigate'Determines how keyboard conflicts in the input are resolved. - "caret": keep native text-editing behavior - "navigate": forward supported keys to listbox navigation |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Content
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Item
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
highlightOnHover | booleanWhether to highlight the item on hover | |
item | anyThe item to render |
ItemText
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
ItemIndicator
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse 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 | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
ItemGroupLabel
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |