ListBox
A listbox displays a list of options and allows a user to select one or more of them.
Content
ListBox
follows the Collection Components API, accepting both static and dynamic collections. This example shows a dynamic collection, passing a list of objects to the items
prop, and a function to render the children.
import {ListBox, ListBoxItem} from 'react-aria-components';
function Example() {
let options = [
{ id: 1, name: 'Aardvark' },
{ id: 2, name: 'Cat' },
{ id: 3, name: 'Dog' },
{ id: 4, name: 'Kangaroo' },
{ id: 5, name: 'Koala' },
{ id: 6, name: 'Penguin' },
{ id: 7, name: 'Snake' },
{ id: 8, name: 'Turtle' },
{ id: 9, name: 'Wombat' }
];
return (
<ListBox aria-label="Animals" items={options} selectionMode="single">
{(item) => <ListBoxItem>{item.name}</ListBoxItem>}
</ListBox>
);
}
Text slots
Use the "label"
and "description"
slots to separate primary and secondary content within a <ListBoxItem>
. This improves screen reader announcements and can also be used for styling purposes.
import {ListBox, ListBoxItem, Text} from 'react-aria-components';
<ListBox aria-label="Permissions" selectionMode="single">
<ListBoxItem textValue="Read">
<Text slot="label">Read</Text>
<Text slot="description">Read only</Text>
</ListBoxItem>
<ListBoxItem textValue="Write">
<Text slot="label">Write</Text>
<Text slot="description">Read and write only</Text>
</ListBoxItem>
<ListBoxItem textValue="Admin">
<Text slot="label">Admin</Text>
<Text slot="description">Full access</Text>
</ListBoxItem>
</ListBox>
Sections
Use the <ListBoxSection>
component to group options. A <Header>
element may also be included to label the section. Sections without a header must have an aria-label
.
import {ListBox, ListBoxItem, ListBoxSection, Header} from 'react-aria-components';
<ListBox aria-label="Sandwich contents" selectionMode="multiple">
<ListBoxSection>
<Header>Veggies</Header>
<ListBoxItem id="lettuce">Lettuce</ListBoxItem>
<ListBoxItem id="tomato">Tomato</ListBoxItem>
<ListBoxItem id="onion">Onion</ListBoxItem>
</ListBoxSection>
<ListBoxSection>
<Header>Protein</Header>
<ListBoxItem id="ham">Ham</ListBoxItem>
<ListBoxItem id="tuna">Tuna</ListBoxItem>
<ListBoxItem id="tofu">Tofu</ListBoxItem>
</ListBoxSection>
<ListBoxSection>
<Header>Condiments</Header>
<ListBoxItem id="mayo">Mayonaise</ListBoxItem>
<ListBoxItem id="mustard">Mustard</ListBoxItem>
<ListBoxItem id="ranch">Ranch</ListBoxItem>
</ListBoxSection>
</ListBox>
Asynchronous loading
Use renderEmptyState to display a spinner during initial load. To enable infinite scrolling, render a <ListBoxLoadMoreItem>
at the end of the list or section. Use whatever data fetching library you prefer – this example uses useAsyncList
from react-stately
.
import {Collection, ListBoxLoadMoreItem} from 'react-aria-components';
import {ListBox, ListBoxItem} from './ListBox';
import {ProgressCircle} from './ProgressCircle';
import {useAsyncList} from 'react-stately';
interface Character {
name: string
}
function AsyncLoadingExample() {
let list = useAsyncList<Character>({
async load({signal, cursor}) {
let res = await fetch(
cursor || `https://pokeapi.co/api/v2/pokemon`,
{ signal }
);
let json = await res.json();
return {
items: json.results,
cursor: json.next
};
}
});
return (
<ListBox
aria-label="Pick a Pokemon"
selectionMode="single"
renderEmptyState={() => (
<ProgressCircle isIndeterminate aria-label="Loading..." />
)}>
<Collection items={list.items}>
{(item) => <ListBoxItem id={item.name}>{item.name}</ListBoxItem>}
</Collection>
<ListBoxLoadMoreItem
onLoadMore={list.loadMore}
isLoading={list.loadingState === 'loadingMore'}>
<ProgressCircle isIndeterminate aria-label="Loading more..." />
</ListBoxLoadMoreItem>
</ListBox>
);
}
Links
Use the href
prop on a <ListBoxItem>
to create a link. See the client side routing guide to learn how to integrate with your framework.
By default, link items in a ListBox are not selectable, and only perform navigation when the user interacts with them. However, with selectionBehavior="replace"
, items will be selected when single clicking or pressing the Space key, and navigate to the link when double clicking or pressing the Enter key.
Empty state
import {ListBox, ListBoxItem} from 'react-aria-components';
<ListBox
aria-label="Search results"
renderEmptyState={() => 'No results found.'}>
{[]}
</ListBox>
Selection
Use the selectionMode
prop to enable single or multiple selection. The selected items can be controlled via the selectedKeys
prop, matching the id
prop of the items. Items can be disabled with the isDisabled
prop. See the selection guide for more details.
Current selection: cheese
Layouts
Use the layout
and orientation
props to create horizontal and vertical stacks and grids. This affects keyboard navigation and drag and drop behavior.
Drag and drop
ListBox supports drag and drop interactions when the dragAndDropHooks
prop is provided using the useDragAndDrop hook. Users can drop data on the list as a whole, on individual items, insert new items between existing ones, or reorder items. React Aria supports drag and drop via mouse, touch, keyboard, and screen reader interactions. See the drag and drop guide to learn more.
import {useListData} from 'react-stately';
import {ListBox, ListBoxItem, useDragAndDrop} from 'react-aria-components';
function Example() {
let list = useListData({
initialItems: [
{id: 1, name: 'Adobe Photoshop'},
{id: 2, name: 'Adobe XD'},
{id: 3, name: 'Adobe Dreamweaver'},
{id: 4, name: 'Adobe InDesign'},
{id: 5, name: 'Adobe Connect'}
]
});
let {dragAndDropHooks} = useDragAndDrop({
getItems: (keys) => [...keys].map(key => ({'text/plain': list.getItem(key).name})),
onReorder(e) {
if (e.target.dropPosition === 'before') {
list.moveBefore(e.target.key, e.keys);
} else if (e.target.dropPosition === 'after') {
list.moveAfter(e.target.key, e.keys);
}
}
});
return (
<ListBox
aria-label="Reorderable list"
selectionMode="multiple"
items={list.items}
dragAndDropHooks={dragAndDropHooks}
>
{item => <ListBoxItem>{item.name}</ListBoxItem>}
</ListBox>
);
}