Beta Preview

ListBox

A listbox displays a list of options and allows a user to select one or more of them.

Aardvark
Cat
Dog
Kangaroo
Panda
Snake
selectionMode 
Example
ListBox.tsx
ListBox.css
import {ListBox, ListBoxItem} from './ListBox';

<ListBox aria-label="Favorite animal">
  <ListBoxItem>Aardvark</ListBoxItem>
  <ListBoxItem>Cat</ListBoxItem>
  <ListBoxItem>Dog</ListBoxItem>
  <ListBoxItem>Kangaroo</ListBoxItem>
  <ListBoxItem>Panda</ListBoxItem>
  <ListBoxItem>Snake</ListBoxItem>
</ListBox>

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.

Aardvark
Cat
Dog
Kangaroo
Koala
Penguin
Snake
Turtle
Wombat
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.

ReadRead only
WriteRead and write only
AdminFull access
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.

Lettuce
Tomato
Onion
Ham
Tuna
Tofu
Mayonaise
Mustard
Ranch
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>
  );
}

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.

selectionBehavior 
import {ListBox, ListBoxItem} from 'react-aria-components';

<ListBox aria-label="Links" selectionMode="multiple">
  <ListBoxItem href="https://adobe.com/" target="_blank">Adobe</ListBoxItem>
  <ListBoxItem href="https://apple.com/" target="_blank">Apple</ListBoxItem>
  <ListBoxItem href="https://google.com/" target="_blank">Google</ListBoxItem>
  <ListBoxItem href="https://microsoft.com/" target="_blank">Microsoft</ListBoxItem>
</ListBox>

Empty state

No results found.
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.

Lettuce
Tomato
Cheese
Tuna Salad
Egg Salad
Ham

Current selection: cheese

selectionMode 
selectionBehavior 
disallowEmptySelection 
import type {Selection} from 'react-aria-components';
import {ListBox, ListBoxItem} from 'react-aria-components';
import {useState} from 'react';

function Example(props) {
  let [selected, setSelected] = useState<Selection>(new Set(['cheese']));

  return (
    <div>
      <ListBox
        {...props}
        aria-label="Sandwich contents"
        selectionMode="multiple"
        selectedKeys={selected}
        onSelectionChange={setSelected}
      >
        <ListBoxItem id="lettuce">Lettuce</ListBoxItem>
        <ListBoxItem id="tomato">Tomato</ListBoxItem>
        <ListBoxItem id="cheese">Cheese</ListBoxItem>
        <ListBoxItem id="tuna" isDisabled>Tuna Salad</ListBoxItem>
        <ListBoxItem id="egg">Egg Salad</ListBoxItem>
        <ListBoxItem id="ham">Ham</ListBoxItem>
      </ListBox>
      <p>Current selection: {selected === 'all' ? 'all' : [...selected].join(', ')}</p>
    </div>
  );
}

Layouts

Use the layout and orientation props to create horizontal and vertical stacks and grids. This affects keyboard navigation and drag and drop behavior.

Euphoric EchoesLuna Solstice
Neon DreamscapeElectra Skyline
Cosmic SerenadeOrion's Symphony
Melancholy MelodiesViolet Mistral
Rhythmic IllusionsMirage Beats
layout 
orientation 
import {ListBox, ListBoxItem, Text} from 'react-aria-components';

<ListBox aria-label="Albums" layout="grid" items={albums} selectionMode="multiple"> {item => ( <ListBoxItem textValue={item.title}> <img src={item.image} alt="" /> <Text slot="label">{item.title}</Text> <Text slot="description">{item.artist}</Text> </ListBoxItem> )} </ListBox>

Drag and drop

ListBox supports drag and drop interactions when the dragAndDropHooks prop is provided using the 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.

Adobe Photoshop
Adobe XD
Adobe Dreamweaver
Adobe InDesign
Adobe Connect
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>
  );
}

API

Option 1Option 2ItemItem labelDescriptionDescriptionOption 3DescriptionItem descriptionSECTION TITLESection headerSection