Select

A select displays a collapsible list of options and allows a user to select one of them.

installyarn add react-aria-components
version1.1.0
usageimport {Select} from 'react-aria-components'

Example#


import {Button, Label, ListBox, ListBoxItem, Popover, Select, SelectValue} from 'react-aria-components';

<Select>
  <Label>Favorite Animal</Label>
  <Button>
    <SelectValue />
    <span aria-hidden="true"></span>
  </Button>
  <Popover>
    <ListBox>
      <ListBoxItem>Aardvark</ListBoxItem>
      <ListBoxItem>Cat</ListBoxItem>
      <ListBoxItem>Dog</ListBoxItem>
      <ListBoxItem>Kangaroo</ListBoxItem>
      <ListBoxItem>Panda</ListBoxItem>
      <ListBoxItem>Snake</ListBoxItem>
    </ListBox>
  </Popover>
</Select>
import {
  Button,
  Label,
  ListBox,
  ListBoxItem,
  Popover,
  Select,
  SelectValue
} from 'react-aria-components';

<Select>
  <Label>Favorite Animal</Label>
  <Button>
    <SelectValue />
    <span aria-hidden="true"></span>
  </Button>
  <Popover>
    <ListBox>
      <ListBoxItem>Aardvark</ListBoxItem>
      <ListBoxItem>Cat</ListBoxItem>
      <ListBoxItem>Dog</ListBoxItem>
      <ListBoxItem>Kangaroo</ListBoxItem>
      <ListBoxItem>Panda</ListBoxItem>
      <ListBoxItem>Snake</ListBoxItem>
    </ListBox>
  </Popover>
</Select>
import {
  Button,
  Label,
  ListBox,
  ListBoxItem,
  Popover,
  Select,
  SelectValue
} from 'react-aria-components';

<Select>
  <Label>
    Favorite Animal
  </Label>
  <Button>
    <SelectValue />
    <span aria-hidden="true"></span>
  </Button>
  <Popover>
    <ListBox>
      <ListBoxItem>
        Aardvark
      </ListBoxItem>
      <ListBoxItem>
        Cat
      </ListBoxItem>
      <ListBoxItem>
        Dog
      </ListBoxItem>
      <ListBoxItem>
        Kangaroo
      </ListBoxItem>
      <ListBoxItem>
        Panda
      </ListBoxItem>
      <ListBoxItem>
        Snake
      </ListBoxItem>
    </ListBox>
  </Popover>
</Select>
Show CSS
@import "@react-aria/example-theme";

.react-aria-Select {
  color: var(--text-color);

  .react-aria-Button {
    box-shadow: 0 1px 2px rgba(0 0 0 / 0.1);
    border-radius: 6px;
    font-size: 1.072rem;
    padding: 0.286rem 0.286rem 0.286rem 0.571rem;
    display: flex;
    align-items: center;
    max-width: 250px;

    &[data-focus-visible] {
      outline: 2px solid var(--focus-ring-color);
      outline-offset: -1px;
    }
  }

  .react-aria-SelectValue {
    &[data-placeholder] {
      font-style: italic;
      color: var(--text-color-placeholder);
    }
  }

  span[aria-hidden] {
    width: 1.5rem;
    line-height: 1.375rem;
    margin-left: 1rem;
    padding: 1px;
    background: var(--highlight-background);
    color: var(--highlight-foreground);
    forced-color-adjust: none;
    border-radius: 4px;
    font-size: 0.857rem;
  }
}

.react-aria-Popover[data-trigger=Select] {
  min-width: var(--trigger-width);

  .react-aria-ListBox {
    display: block;
    width: unset;
    max-height: inherit;
    min-height: unset;
    border: none;

    .react-aria-Header {
      padding-left: 1.571rem;
    }
  }

  .react-aria-ListBoxItem {
    padding: 0.286rem 0.571rem 0.286rem 1.571rem;

    &[data-focus-visible] {
      outline: none;
    }

    &[data-selected] {
      font-weight: 600;
      background: unset;
      color: var(--text-color);

      &::before {
        content: '✓';
        content: '✓' / '';
        alt: ' ';
        position: absolute;
        top: 4px;
        left: 4px;
      }
    }

    &[data-focused],
    &[data-pressed] {
      background: var(--highlight-background);
      color: var(--highlight-foreground);
    }
  }
}
@import "@react-aria/example-theme";

.react-aria-Select {
  color: var(--text-color);

  .react-aria-Button {
    box-shadow: 0 1px 2px rgba(0 0 0 / 0.1);
    border-radius: 6px;
    font-size: 1.072rem;
    padding: 0.286rem 0.286rem 0.286rem 0.571rem;
    display: flex;
    align-items: center;
    max-width: 250px;

    &[data-focus-visible] {
      outline: 2px solid var(--focus-ring-color);
      outline-offset: -1px;
    }
  }

  .react-aria-SelectValue {
    &[data-placeholder] {
      font-style: italic;
      color: var(--text-color-placeholder);
    }
  }

  span[aria-hidden] {
    width: 1.5rem;
    line-height: 1.375rem;
    margin-left: 1rem;
    padding: 1px;
    background: var(--highlight-background);
    color: var(--highlight-foreground);
    forced-color-adjust: none;
    border-radius: 4px;
    font-size: 0.857rem;
  }
}

.react-aria-Popover[data-trigger=Select] {
  min-width: var(--trigger-width);

  .react-aria-ListBox {
    display: block;
    width: unset;
    max-height: inherit;
    min-height: unset;
    border: none;

    .react-aria-Header {
      padding-left: 1.571rem;
    }
  }

  .react-aria-ListBoxItem {
    padding: 0.286rem 0.571rem 0.286rem 1.571rem;

    &[data-focus-visible] {
      outline: none;
    }

    &[data-selected] {
      font-weight: 600;
      background: unset;
      color: var(--text-color);

      &::before {
        content: '✓';
        content: '✓' / '';
        alt: ' ';
        position: absolute;
        top: 4px;
        left: 4px;
      }
    }

    &[data-focused],
    &[data-pressed] {
      background: var(--highlight-background);
      color: var(--highlight-foreground);
    }
  }
}
@import "@react-aria/example-theme";

.react-aria-Select {
  color: var(--text-color);

  .react-aria-Button {
    box-shadow: 0 1px 2px rgba(0 0 0 / 0.1);
    border-radius: 6px;
    font-size: 1.072rem;
    padding: 0.286rem 0.286rem 0.286rem 0.571rem;
    display: flex;
    align-items: center;
    max-width: 250px;

    &[data-focus-visible] {
      outline: 2px solid var(--focus-ring-color);
      outline-offset: -1px;
    }
  }

  .react-aria-SelectValue {
    &[data-placeholder] {
      font-style: italic;
      color: var(--text-color-placeholder);
    }
  }

  span[aria-hidden] {
    width: 1.5rem;
    line-height: 1.375rem;
    margin-left: 1rem;
    padding: 1px;
    background: var(--highlight-background);
    color: var(--highlight-foreground);
    forced-color-adjust: none;
    border-radius: 4px;
    font-size: 0.857rem;
  }
}

.react-aria-Popover[data-trigger=Select] {
  min-width: var(--trigger-width);

  .react-aria-ListBox {
    display: block;
    width: unset;
    max-height: inherit;
    min-height: unset;
    border: none;

    .react-aria-Header {
      padding-left: 1.571rem;
    }
  }

  .react-aria-ListBoxItem {
    padding: 0.286rem 0.571rem 0.286rem 1.571rem;

    &[data-focus-visible] {
      outline: none;
    }

    &[data-selected] {
      font-weight: 600;
      background: unset;
      color: var(--text-color);

      &::before {
        content: '✓';
        content: '✓' / '';
        alt: ' ';
        position: absolute;
        top: 4px;
        left: 4px;
      }
    }

    &[data-focused],
    &[data-pressed] {
      background: var(--highlight-background);
      color: var(--highlight-foreground);
    }
  }
}

Features#


A select can be built using the <select> and <option> HTML elements, but this is not possible to style consistently cross browser, especially the options. Select helps achieve accessible select components that can be styled as needed without compromising on high quality interactions.

  • Flexible – Support for controlled and uncontrolled state, async loading, disabled items, validation, sections, complex items, and more.
  • Keyboard navigation – Select can be opened and navigated using the arrow keys, along with page up/down, home/end, etc. Auto scrolling, and typeahead both in the listbox and on the button, are supported as well.
  • Accessible – Follows the ARIA listbox pattern, with support for items and sections, and slots for label and description elements within each item for improved screen reader announcement.
  • HTML form integration – A visually hidden <select> element is included to enable HTML form integration, autofill, and mobile form navigation via the software keyboard.
  • Validation – Support for native HTML constraint validation with customizable UI, custom validation functions, and server-side validation errors.
  • Styleable – Items include builtin states for styling, such as hover, press, focus, selected, and disabled.

Anatomy#


Option 1Option 1LabelLabelButtonLabelLabelOption 1Option 2ItemItem labelDescriptionDescriptionOption 3DescriptionItem descriptionSECTION TITLESection headerSectionPopoverListBoxValueHelp text​​Help text (description or error message)

A select consists of a label, a button which displays a selected value, and a listbox, displayed in a popover. Users can click, touch, or use the keyboard on the button to open the listbox popover.

Select also supports optional description and error message elements, which can be used to provide more context about the field, and any validation messages. These are linked with the input via the aria-describedby attribute.

import {Button, FieldError, Header, Label, ListBox, ListBoxItem, Popover, Section, Select, SelectValue, Text} from 'react-aria-components';

<Select>
  <Label />
  <Button>
    <SelectValue />
  </Button>
  <Text slot="description" />
  <FieldError />
  <Popover>
    <ListBox>
      <ListBoxItem>
        <Text slot="label" />
        <Text slot="description" />
      </ListBoxItem>
      <Section>
        <Header />
        <ListBoxItem />
      </Section>
    </ListBox>
  </Popover>
</Select>
import {
  Button,
  FieldError,
  Header,
  Label,
  ListBox,
  ListBoxItem,
  Popover,
  Section,
  Select,
  SelectValue,
  Text
} from 'react-aria-components';

<Select>
  <Label />
  <Button>
    <SelectValue />
  </Button>
  <Text slot="description" />
  <FieldError />
  <Popover>
    <ListBox>
      <ListBoxItem>
        <Text slot="label" />
        <Text slot="description" />
      </ListBoxItem>
      <Section>
        <Header />
        <ListBoxItem />
      </Section>
    </ListBox>
  </Popover>
</Select>
import {
  Button,
  FieldError,
  Header,
  Label,
  ListBox,
  ListBoxItem,
  Popover,
  Section,
  Select,
  SelectValue,
  Text
} from 'react-aria-components';

<Select>
  <Label />
  <Button>
    <SelectValue />
  </Button>
  <Text slot="description" />
  <FieldError />
  <Popover>
    <ListBox>
      <ListBoxItem>
        <Text slot="label" />
        <Text slot="description" />
      </ListBoxItem>
      <Section>
        <Header />
        <ListBoxItem />
      </Section>
    </ListBox>
  </Popover>
</Select>

If a select does not have a visible label, an aria-label or aria-labelledby prop must be passed instead to identify it to assistive technology.

Concepts#

Select makes use of the following concepts:

Collections
Defining collections of items, async loading, and updating items over time.
Selection
Interactions and data structures to represent selection.
Forms
Validating and submitting form data, and integrating with form libraries.

Composed components#

A Select uses the following components, which may also be used standalone or reused in other components.

Label
A label provides context for an input element.
Button
A button allows a user to perform an action.
Popover
A popover displays content in context with a trigger element.
ListBox
A listbox allows a user to select one or more options from a list.

Examples#


Status Select
An issue status dropdown styled with Tailwind CSS.

Starter kits#


To help kick-start your project, we offer starter kits that include example implementations of all React Aria components with various styling solutions. All components are fully styled, including support for dark mode, high contrast mode, and all UI states. Each starter comes with a pre-configured Storybook that you can experiment with, or use as a starting point for your own component library.

Vanilla CSS
Download ZIP
Preview
Tailwind CSS
Download ZIP
Preview

Reusable wrappers#


If you will use a Select in multiple places in your app, you can wrap all of the pieces into a reusable component. This way, the DOM structure, styling code, and other logic are defined in a single place and reused everywhere to ensure consistency.

This example wraps Select and all of its children together into a single component which accepts a label prop and children, which are passed through to the right places. It also shows how to use the description slot to render help text, and FieldError component to render validation errors. The Item component is also wrapped to apply class names based on the current state, as described in the styling section.

import type {ListBoxItemProps, SelectProps, ValidationResult} from 'react-aria-components';
import {FieldError, Text} from 'react-aria-components';

interface MySelectProps<T extends object>
  extends Omit<SelectProps<T>, 'children'> {
  label?: string;
  description?: string;
  errorMessage?: string | ((validation: ValidationResult) => string);
  items?: Iterable<T>;
  children: React.ReactNode | ((item: T) => React.ReactNode);
}

function MySelect<T extends object>(
  { label, description, errorMessage, children, items, ...props }:
    MySelectProps<T>
) {
  return (
    <Select {...props}>
      <Label>{label}</Label>
      <Button>
        <SelectValue />
        <span aria-hidden="true"></span>
      </Button>
      {description && <Text slot="description">{description}</Text>}
      <FieldError>{errorMessage}</FieldError>
      <Popover>
        <ListBox items={items}>
          {children}
        </ListBox>
      </Popover>
    </Select>
  );
}

function MyItem(props: ListBoxItemProps) {
  return (
    <ListBoxItem
      {...props}
      className={({ isFocused, isSelected }) =>
        `my-item ${isFocused ? 'focused' : ''} ${isSelected ? 'selected' : ''}`}
    />
  );
}

<MySelect label="Ice cream flavor">
  <MyItem>Chocolate</MyItem>
  <MyItem>Mint</MyItem>
  <MyItem>Strawberry</MyItem>
  <MyItem>Vanilla</MyItem>
</MySelect>
import type {
  ListBoxItemProps,
  SelectProps,
  ValidationResult
} from 'react-aria-components';
import {FieldError, Text} from 'react-aria-components';

interface MySelectProps<T extends object>
  extends Omit<SelectProps<T>, 'children'> {
  label?: string;
  description?: string;
  errorMessage?:
    | string
    | ((validation: ValidationResult) => string);
  items?: Iterable<T>;
  children:
    | React.ReactNode
    | ((item: T) => React.ReactNode);
}

function MySelect<T extends object>(
  {
    label,
    description,
    errorMessage,
    children,
    items,
    ...props
  }: MySelectProps<T>
) {
  return (
    <Select {...props}>
      <Label>{label}</Label>
      <Button>
        <SelectValue />
        <span aria-hidden="true"></span>
      </Button>
      {description && (
        <Text slot="description">{description}</Text>
      )}
      <FieldError>{errorMessage}</FieldError>
      <Popover>
        <ListBox items={items}>
          {children}
        </ListBox>
      </Popover>
    </Select>
  );
}

function MyItem(props: ListBoxItemProps) {
  return (
    <ListBoxItem
      {...props}
      className={({ isFocused, isSelected }) =>
        `my-item ${isFocused ? 'focused' : ''} ${
          isSelected ? 'selected' : ''
        }`}
    />
  );
}

<MySelect label="Ice cream flavor">
  <MyItem>Chocolate</MyItem>
  <MyItem>Mint</MyItem>
  <MyItem>Strawberry</MyItem>
  <MyItem>Vanilla</MyItem>
</MySelect>
import type {
  ListBoxItemProps,
  SelectProps,
  ValidationResult
} from 'react-aria-components';
import {
  FieldError,
  Text
} from 'react-aria-components';

interface MySelectProps<
  T extends object
> extends
  Omit<
    SelectProps<T>,
    'children'
  > {
  label?: string;
  description?: string;
  errorMessage?:
    | string
    | ((
      validation:
        ValidationResult
    ) => string);
  items?: Iterable<T>;
  children:
    | React.ReactNode
    | ((
      item: T
    ) =>
      React.ReactNode);
}

function MySelect<
  T extends object
>(
  {
    label,
    description,
    errorMessage,
    children,
    items,
    ...props
  }: MySelectProps<T>
) {
  return (
    <Select {...props}>
      <Label>
        {label}
      </Label>
      <Button>
        <SelectValue />
        <span aria-hidden="true"></span>
      </Button>
      {description && (
        <Text slot="description">
          {description}
        </Text>
      )}
      <FieldError>
        {errorMessage}
      </FieldError>
      <Popover>
        <ListBox
          items={items}
        >
          {children}
        </ListBox>
      </Popover>
    </Select>
  );
}

function MyItem(
  props: ListBoxItemProps
) {
  return (
    <ListBoxItem
      {...props}
      className={(
        {
          isFocused,
          isSelected
        }
      ) =>
        `my-item ${
          isFocused
            ? 'focused'
            : ''
        } ${
          isSelected
            ? 'selected'
            : ''
        }`}
    />
  );
}

<MySelect label="Ice cream flavor">
  <MyItem>
    Chocolate
  </MyItem>
  <MyItem>Mint</MyItem>
  <MyItem>
    Strawberry
  </MyItem>
  <MyItem>
    Vanilla
  </MyItem>
</MySelect>
Show CSS
.my-item {
  margin: 2px;
  padding: 4px 8px 4px 22px;
  border-radius: 6px;
  outline: none;
  cursor: default;
  color: var(--text-color);
  font-size: 1.072rem;
  position: relative;

  &.selected {
    font-weight: 600;
    background: none;

    &::before {
      content: '✓';
      content: '✓' / '';
      alt: ' ';
      position: absolute;
      top: 4px;
      left: 4px;
    }
  }

  &.focused {
    background: #e70073;
    color: white;
  }
}

@media (forced-colors: active) {
  .my-item.focused {
    background: Highlight;
    color: HighlightText;
  }
}
.my-item {
  margin: 2px;
  padding: 4px 8px 4px 22px;
  border-radius: 6px;
  outline: none;
  cursor: default;
  color: var(--text-color);
  font-size: 1.072rem;
  position: relative;

  &.selected {
    font-weight: 600;
    background: none;

    &::before {
      content: '✓';
      content: '✓' / '';
      alt: ' ';
      position: absolute;
      top: 4px;
      left: 4px;
    }
  }

  &.focused {
    background: #e70073;
    color: white;
  }
}

@media (forced-colors: active) {
  .my-item.focused {
    background: Highlight;
    color: HighlightText;
  }
}
.my-item {
  margin: 2px;
  padding: 4px 8px 4px 22px;
  border-radius: 6px;
  outline: none;
  cursor: default;
  color: var(--text-color);
  font-size: 1.072rem;
  position: relative;

  &.selected {
    font-weight: 600;
    background: none;

    &::before {
      content: '✓';
      content: '✓' / '';
      alt: ' ';
      position: absolute;
      top: 4px;
      left: 4px;
    }
  }

  &.focused {
    background: #e70073;
    color: white;
  }
}

@media (forced-colors: active) {
  .my-item.focused {
    background: Highlight;
    color: HighlightText;
  }
}

Content#


Select follows the Collection Components API, accepting both static and dynamic collections. The examples above show static collections, which can be used when the full list of options is known ahead of time. Dynamic collections, as shown below, can be used when the options come from an external data source such as an API call, or update over time.

As seen below, an iterable list of options is passed to the Select using the items prop. Each item accepts an id prop, which is passed to the onSelectionChange handler to identify the selected item. Alternatively, if the item objects contain an id property, as shown in the example below, then this is used automatically and an id prop is not required.

function Example() {
  let options = [
    {id: 1, name: 'Aerospace'},
    {id: 2, name: 'Mechanical'},
    {id: 3, name: 'Civil'},
    {id: 4, name: 'Biomedical'},
    {id: 5, name: 'Nuclear'},
    {id: 6, name: 'Industrial'},
    {id: 7, name: 'Chemical'},
    {id: 8, name: 'Agricultural'},
    {id: 9, name: 'Electrical'}
  ];

  return (
    <MySelect label="Pick an engineering major" items={options}>
      {(item) => <ListBoxItem>{item.name}</ListBoxItem>}
    </MySelect>
  );
}
function Example() {
  let options = [
    { id: 1, name: 'Aerospace' },
    { id: 2, name: 'Mechanical' },
    { id: 3, name: 'Civil' },
    { id: 4, name: 'Biomedical' },
    { id: 5, name: 'Nuclear' },
    { id: 6, name: 'Industrial' },
    { id: 7, name: 'Chemical' },
    { id: 8, name: 'Agricultural' },
    { id: 9, name: 'Electrical' }
  ];

  return (
    <MySelect
      label="Pick an engineering major"
      items={options}
    >
      {(item) => <ListBoxItem>{item.name}</ListBoxItem>}
    </MySelect>
  );
}
function Example() {
  let options = [
    {
      id: 1,
      name: 'Aerospace'
    },
    {
      id: 2,
      name: 'Mechanical'
    },
    {
      id: 3,
      name: 'Civil'
    },
    {
      id: 4,
      name: 'Biomedical'
    },
    {
      id: 5,
      name: 'Nuclear'
    },
    {
      id: 6,
      name: 'Industrial'
    },
    {
      id: 7,
      name: 'Chemical'
    },
    {
      id: 8,
      name:
        'Agricultural'
    },
    {
      id: 9,
      name: 'Electrical'
    }
  ];

  return (
    <MySelect
      label="Pick an engineering major"
      items={options}
    >
      {(item) => (
        <ListBoxItem>
          {item.name}
        </ListBoxItem>
      )}
    </MySelect>
  );
}

Selection#


Setting a selected option can be done by using the defaultSelectedKey or selectedKey prop. The selected key corresponds to the id prop of an item. When Select is used with a dynamic collection as described above, the id of each item is derived from the data. See the Selection guide for more details.

import type {Key} from 'react-aria-components';

function Example() {
  let options = [
    {name: 'Koala'},
    {name: 'Kangaroo'},
    {name: 'Platypus'},
    {name: 'Bald Eagle'},
    {name: 'Bison'},
    {name: 'Skunk'}
  ];
  let [animal, setAnimal] = React.useState<Key>("Bison");

  return (
    <MySelect
      label="Pick an animal (controlled)"
      items={options}
      selectedKey={animal}
      onSelectionChange={selected => setAnimal(selected)}>
      {item => <ListBoxItem id={item.name}>{item.name}</ListBoxItem>}
    </MySelect>
  );
}
import type {Key} from 'react-aria-components';

function Example() {
  let options = [
    { name: 'Koala' },
    { name: 'Kangaroo' },
    { name: 'Platypus' },
    { name: 'Bald Eagle' },
    { name: 'Bison' },
    { name: 'Skunk' }
  ];
  let [animal, setAnimal] = React.useState<Key>('Bison');

  return (
    <MySelect
      label="Pick an animal (controlled)"
      items={options}
      selectedKey={animal}
      onSelectionChange={(selected) => setAnimal(selected)}
    >
      {(item) => (
        <ListBoxItem id={item.name}>
          {item.name}
        </ListBoxItem>
      )}
    </MySelect>
  );
}
import type {Key} from 'react-aria-components';

function Example() {
  let options = [
    { name: 'Koala' },
    { name: 'Kangaroo' },
    { name: 'Platypus' },
    {
      name: 'Bald Eagle'
    },
    { name: 'Bison' },
    { name: 'Skunk' }
  ];
  let [
    animal,
    setAnimal
  ] = React.useState<
    Key
  >('Bison');

  return (
    <MySelect
      label="Pick an animal (controlled)"
      items={options}
      selectedKey={animal}
      onSelectionChange={(selected) =>
        setAnimal(
          selected
        )}
    >
      {(item) => (
        <ListBoxItem
          id={item.name}
        >
          {item.name}
        </ListBoxItem>
      )}
    </MySelect>
  );
}

HTML forms#

Select supports the name prop for integration with HTML forms. The id of the selected item will be submitted to the server.

<MySelect
  label="Favorite Animal"
  name="favoriteAnimalId">
  <ListBoxItem id="panda">Panda</ListBoxItem>
  <ListBoxItem id="cat">Cat</ListBoxItem>
  <ListBoxItem id="dog">Dog</ListBoxItem>
</MySelect>
<MySelect
  label="Favorite Animal"
  name="favoriteAnimalId">
  <ListBoxItem id="panda">Panda</ListBoxItem>
  <ListBoxItem id="cat">Cat</ListBoxItem>
  <ListBoxItem id="dog">Dog</ListBoxItem>
</MySelect>
<MySelect
  label="Favorite Animal"
  name="favoriteAnimalId">
  <ListBoxItem id="panda">
    Panda
  </ListBoxItem>
  <ListBoxItem id="cat">
    Cat
  </ListBoxItem>
  <ListBoxItem id="dog">
    Dog
  </ListBoxItem>
</MySelect>

By default, interacting with an item in a Select triggers onSelectionChange. Alternatively, items may be links to another page or website. This can be achieved by passing the href prop to the <ListBoxItem> component. Link items in a Select are not selectable.

<MySelect label="Project">
  <ListBoxItem href="https://example.com/" target="_blank">
    Create new…
  </ListBoxItem>
  <ListBoxItem>Proposal</ListBoxItem>
  <ListBoxItem>Budget</ListBoxItem>
  <ListBoxItem>Onboarding</ListBoxItem>
</MySelect>
<MySelect label="Project">
  <ListBoxItem href="https://example.com/" target="_blank">
    Create new…
  </ListBoxItem>
  <ListBoxItem>Proposal</ListBoxItem>
  <ListBoxItem>Budget</ListBoxItem>
  <ListBoxItem>Onboarding</ListBoxItem>
</MySelect>
<MySelect label="Project">
  <ListBoxItem
    href="https://example.com/"
    target="_blank"
  >
    Create new…
  </ListBoxItem>
  <ListBoxItem>
    Proposal
  </ListBoxItem>
  <ListBoxItem>
    Budget
  </ListBoxItem>
  <ListBoxItem>
    Onboarding
  </ListBoxItem>
</MySelect>

Client side routing#

The <ListBoxItem> component works with frameworks and client side routers like Next.js and React Router. As with other React Aria components that support links, this works via the RouterProvider component at the root of your app. See the client side routing guide to learn how to set this up.

Sections#


Select supports sections in order to group options. Sections can be used by wrapping groups of items in a Section element. A <Header> element may also be included to label the section.

Static items#

import {Section, Header} from 'react-aria-components';

<MySelect label="Preferred fruit or vegetable">
  <Section>
    <Header>Fruit</Header>
    <ListBoxItem id="Apple">Apple</ListBoxItem>
    <ListBoxItem id="Banana">Banana</ListBoxItem>
    <ListBoxItem id="Orange">Orange</ListBoxItem>
    <ListBoxItem id="Honeydew">Honeydew</ListBoxItem>
    <ListBoxItem id="Grapes">Grapes</ListBoxItem>
    <ListBoxItem id="Watermelon">Watermelon</ListBoxItem>
    <ListBoxItem id="Cantaloupe">Cantaloupe</ListBoxItem>
    <ListBoxItem id="Pear">Pear</ListBoxItem>
  </Section>
  <Section>
    <Header>Vegetable</Header>
    <ListBoxItem id="Cabbage">Cabbage</ListBoxItem>
    <ListBoxItem id="Broccoli">Broccoli</ListBoxItem>
    <ListBoxItem id="Carrots">Carrots</ListBoxItem>
    <ListBoxItem id="Lettuce">Lettuce</ListBoxItem>
    <ListBoxItem id="Spinach">Spinach</ListBoxItem>
    <ListBoxItem id="Bok Choy">Bok Choy</ListBoxItem>
    <ListBoxItem id="Cauliflower">Cauliflower</ListBoxItem>
    <ListBoxItem id="Potatoes">Potatoes</ListBoxItem>
  </Section>
</MySelect>
import {Section, Header} from 'react-aria-components';

<MySelect label="Preferred fruit or vegetable">
  <Section>
    <Header>Fruit</Header>
    <ListBoxItem id="Apple">Apple</ListBoxItem>
    <ListBoxItem id="Banana">Banana</ListBoxItem>
    <ListBoxItem id="Orange">Orange</ListBoxItem>
    <ListBoxItem id="Honeydew">Honeydew</ListBoxItem>
    <ListBoxItem id="Grapes">Grapes</ListBoxItem>
    <ListBoxItem id="Watermelon">Watermelon</ListBoxItem>
    <ListBoxItem id="Cantaloupe">Cantaloupe</ListBoxItem>
    <ListBoxItem id="Pear">Pear</ListBoxItem>
  </Section>
  <Section>
    <Header>Vegetable</Header>
    <ListBoxItem id="Cabbage">Cabbage</ListBoxItem>
    <ListBoxItem id="Broccoli">Broccoli</ListBoxItem>
    <ListBoxItem id="Carrots">Carrots</ListBoxItem>
    <ListBoxItem id="Lettuce">Lettuce</ListBoxItem>
    <ListBoxItem id="Spinach">Spinach</ListBoxItem>
    <ListBoxItem id="Bok Choy">Bok Choy</ListBoxItem>
    <ListBoxItem id="Cauliflower">Cauliflower</ListBoxItem>
    <ListBoxItem id="Potatoes">Potatoes</ListBoxItem>
  </Section>
</MySelect>
import {
  Header,
  Section
} from 'react-aria-components';

<MySelect label="Preferred fruit or vegetable">
  <Section>
    <Header>
      Fruit
    </Header>
    <ListBoxItem id="Apple">
      Apple
    </ListBoxItem>
    <ListBoxItem id="Banana">
      Banana
    </ListBoxItem>
    <ListBoxItem id="Orange">
      Orange
    </ListBoxItem>
    <ListBoxItem id="Honeydew">
      Honeydew
    </ListBoxItem>
    <ListBoxItem id="Grapes">
      Grapes
    </ListBoxItem>
    <ListBoxItem id="Watermelon">
      Watermelon
    </ListBoxItem>
    <ListBoxItem id="Cantaloupe">
      Cantaloupe
    </ListBoxItem>
    <ListBoxItem id="Pear">
      Pear
    </ListBoxItem>
  </Section>
  <Section>
    <Header>
      Vegetable
    </Header>
    <ListBoxItem id="Cabbage">
      Cabbage
    </ListBoxItem>
    <ListBoxItem id="Broccoli">
      Broccoli
    </ListBoxItem>
    <ListBoxItem id="Carrots">
      Carrots
    </ListBoxItem>
    <ListBoxItem id="Lettuce">
      Lettuce
    </ListBoxItem>
    <ListBoxItem id="Spinach">
      Spinach
    </ListBoxItem>
    <ListBoxItem id="Bok Choy">
      Bok Choy
    </ListBoxItem>
    <ListBoxItem id="Cauliflower">
      Cauliflower
    </ListBoxItem>
    <ListBoxItem id="Potatoes">
      Potatoes
    </ListBoxItem>
  </Section>
</MySelect>

Dynamic items#

Sections used with dynamic items are populated from a hierarchical data structure. Similarly to the props on Select, <Section> takes an array of data using the items prop. If the section also has a header, the Collection component can be used to render the child items.

import {Collection} from 'react-aria-components';

function Example() {
  let options = [
    {name: 'Fruit', children: [
      {name: 'Apple'},
      {name: 'Banana'},
      {name: 'Orange'},
      {name: 'Honeydew'},
      {name: 'Grapes'},
      {name: 'Watermelon'},
      {name: 'Cantaloupe'},
      {name: 'Pear'}
    ]},
    {name: 'Vegetable', children: [
      {name: 'Cabbage'},
      {name: 'Broccoli'},
      {name: 'Carrots'},
      {name: 'Lettuce'},
      {name: 'Spinach'},
      {name: 'Bok Choy'},
      {name: 'Cauliflower'},
      {name: 'Potatoes'}
    ]}
  ];

  return (
    <MySelect label="Preferred fruit or vegetable" items={options}>
      {section => (
        <Section id={section.name}>
          <Header>{section.name}</Header>
          <Collection items={section.children}>
            {item => <ListBoxItem id={item.name}>{item.name}</ListBoxItem>}
          </Collection>
        </Section>
      )}
    </MySelect>
  );
}
import {Collection} from 'react-aria-components';

function Example() {
  let options = [
    {
      name: 'Fruit',
      children: [
        { name: 'Apple' },
        { name: 'Banana' },
        { name: 'Orange' },
        { name: 'Honeydew' },
        { name: 'Grapes' },
        { name: 'Watermelon' },
        { name: 'Cantaloupe' },
        { name: 'Pear' }
      ]
    },
    {
      name: 'Vegetable',
      children: [
        { name: 'Cabbage' },
        { name: 'Broccoli' },
        { name: 'Carrots' },
        { name: 'Lettuce' },
        { name: 'Spinach' },
        { name: 'Bok Choy' },
        { name: 'Cauliflower' },
        { name: 'Potatoes' }
      ]
    }
  ];

  return (
    <MySelect
      label="Preferred fruit or vegetable"
      items={options}
    >
      {(section) => (
        <Section id={section.name}>
          <Header>{section.name}</Header>
          <Collection items={section.children}>
            {(item) => (
              <ListBoxItem id={item.name}>
                {item.name}
              </ListBoxItem>
            )}
          </Collection>
        </Section>
      )}
    </MySelect>
  );
}
import {Collection} from 'react-aria-components';

function Example() {
  let options = [
    {
      name: 'Fruit',
      children: [
        {
          name: 'Apple'
        },
        {
          name: 'Banana'
        },
        {
          name: 'Orange'
        },
        {
          name:
            'Honeydew'
        },
        {
          name: 'Grapes'
        },
        {
          name:
            'Watermelon'
        },
        {
          name:
            'Cantaloupe'
        },
        { name: 'Pear' }
      ]
    },
    {
      name: 'Vegetable',
      children: [
        {
          name: 'Cabbage'
        },
        {
          name:
            'Broccoli'
        },
        {
          name: 'Carrots'
        },
        {
          name: 'Lettuce'
        },
        {
          name: 'Spinach'
        },
        {
          name:
            'Bok Choy'
        },
        {
          name:
            'Cauliflower'
        },
        {
          name:
            'Potatoes'
        }
      ]
    }
  ];

  return (
    <MySelect
      label="Preferred fruit or vegetable"
      items={options}
    >
      {(section) => (
        <Section
          id={section
            .name}
        >
          <Header>
            {section
              .name}
          </Header>
          <Collection
            items={section
              .children}
          >
            {(item) => (
              <ListBoxItem
                id={item
                  .name}
              >
                {item
                  .name}
              </ListBoxItem>
            )}
          </Collection>
        </Section>
      )}
    </MySelect>
  );
}

Text slots#


By default, items in a Select are labeled by their text contents for accessibility. ListBoxItems also support the "label" and "description" slots to separate primary and secondary content, which improves screen reader announcements and can also be used for styling purposes.

Note: The ARIA spec prohibits listbox items from including interactive content such as buttons, checkboxes, etc.

import {Text} from 'react-aria-components';

<MySelect label="Permissions">
  <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>
</MySelect>
import {Text} from 'react-aria-components';

<MySelect label="Permissions">
  <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>
</MySelect>
import {Text} from 'react-aria-components';

<MySelect label="Permissions">
  <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>
</MySelect>
Show CSS
.react-aria-Select {
  .react-aria-SelectValue {
    [slot=description] {
      display: none;
    }
  }
}
.react-aria-Select {
  .react-aria-SelectValue {
    [slot=description] {
      display: none;
    }
  }
}
.react-aria-Select {
  .react-aria-SelectValue {
    [slot=description] {
      display: none;
    }
  }
}

Asynchronous loading#


This example uses the useAsyncList hook to handle asynchronous loading of data from a server. You may additionally want to display a spinner to indicate the loading state to the user.

import {useAsyncList} from 'react-stately';

interface Character {
  name: string;
}

function AsyncLoadingExample() {
  let list = useAsyncList<Character>({
    async load({ signal, filterText }) {
      let res = await fetch(
        `https://pokeapi.co/api/v2/pokemon`,
        { signal }
      );
      let json = await res.json();

      return {
        items: json.results
      };
    }
  });

  return (
    <MySelect label="Pick a Pokemon" items={list.items}>
      {(item) => <ListBoxItem id={item.name}>{item.name}</ListBoxItem>}
    </MySelect>
  );
}
import {useAsyncList} from 'react-stately';

interface Character {
  name: string;
}

function AsyncLoadingExample() {
  let list = useAsyncList<Character>({
    async load({ signal, filterText }) {
      let res = await fetch(
        `https://pokeapi.co/api/v2/pokemon`,
        { signal }
      );
      let json = await res.json();

      return {
        items: json.results
      };
    }
  });

  return (
    <MySelect label="Pick a Pokemon" items={list.items}>
      {(item) => (
        <ListBoxItem id={item.name}>
          {item.name}
        </ListBoxItem>
      )}
    </MySelect>
  );
}
import {useAsyncList} from 'react-stately';

interface Character {
  name: string;
}

function AsyncLoadingExample() {
  let list =
    useAsyncList<
      Character
    >({
      async load(
        {
          signal,
          filterText
        }
      ) {
        let res =
          await fetch(
            `https://pokeapi.co/api/v2/pokemon`,
            { signal }
          );
        let json =
          await res
            .json();

        return {
          items:
            json.results
        };
      }
    });

  return (
    <MySelect
      label="Pick a Pokemon"
      items={list.items}
    >
      {(item) => (
        <ListBoxItem
          id={item.name}
        >
          {item.name}
        </ListBoxItem>
      )}
    </MySelect>
  );
}

Disabled#


A Select can be fully disabled using the isDisabled prop.

<MySelect label="Choose frequency" isDisabled>
  <ListBoxItem id="rarely">Rarely</ListBoxItem>
  <ListBoxItem id="sometimes">Sometimes</ListBoxItem>
  <ListBoxItem id="always">Always</ListBoxItem>
</MySelect>
<MySelect label="Choose frequency" isDisabled>
  <ListBoxItem id="rarely">Rarely</ListBoxItem>
  <ListBoxItem id="sometimes">Sometimes</ListBoxItem>
  <ListBoxItem id="always">Always</ListBoxItem>
</MySelect>
<MySelect
  label="Choose frequency"
  isDisabled
>
  <ListBoxItem id="rarely">
    Rarely
  </ListBoxItem>
  <ListBoxItem id="sometimes">
    Sometimes
  </ListBoxItem>
  <ListBoxItem id="always">
    Always
  </ListBoxItem>
</MySelect>
Show CSS
.react-aria-Select {
  .react-aria-Button {
    &[data-disabled] {
      border-color: var(--border-color-disabled);
      color: var(--text-color-disabled);
      span[aria-hidden] {
        background: var(--border-color-disabled);
        color: var(--text-color-disabled);
      }

      .react-aria-SelectValue {
        &[data-placeholder] {
          color: var(--text-color-disabled);
        }
      }
    }
  }
}

@media (forced-colors: active) {
  .react-aria-Select {
    .react-aria-Button {
      &[data-disabled] span[aria-hidden] {
        background: 0 0;
      }
    }
  }
}
.react-aria-Select {
  .react-aria-Button {
    &[data-disabled] {
      border-color: var(--border-color-disabled);
      color: var(--text-color-disabled);
      span[aria-hidden] {
        background: var(--border-color-disabled);
        color: var(--text-color-disabled);
      }

      .react-aria-SelectValue {
        &[data-placeholder] {
          color: var(--text-color-disabled);
        }
      }
    }
  }
}

@media (forced-colors: active) {
  .react-aria-Select {
    .react-aria-Button {
      &[data-disabled] span[aria-hidden] {
        background: 0 0;
      }
    }
  }
}
.react-aria-Select {
  .react-aria-Button {
    &[data-disabled] {
      border-color: var(--border-color-disabled);
      color: var(--text-color-disabled);
      span[aria-hidden] {
        background: var(--border-color-disabled);
        color: var(--text-color-disabled);
      }

      .react-aria-SelectValue {
        &[data-placeholder] {
          color: var(--text-color-disabled);
        }
      }
    }
  }
}

@media (forced-colors: active) {
  .react-aria-Select {
    .react-aria-Button {
      &[data-disabled] span[aria-hidden] {
        background: 0 0;
      }
    }
  }
}

Disabled options#

Select supports marking items as disabled using the disabledKeys prop. Each key in this list corresponds with the id prop passed to the ListBoxItem component, or automatically derived from the values passed to the items prop. See the Collections guide for more details.

Disabled items are not focusable, selectable, or keyboard navigable. The isDisabled property returned by useOption can be used to style the item appropriately.

<MySelect label="Favorite Animal" disabledKeys={['cat', 'kangaroo']}>
  <ListBoxItem id="red panda">Red Panda</ListBoxItem>
  <ListBoxItem id="cat">Cat</ListBoxItem>
  <ListBoxItem id="dog">Dog</ListBoxItem>
  <ListBoxItem id="aardvark">Aardvark</ListBoxItem>
  <ListBoxItem id="kangaroo">Kangaroo</ListBoxItem>
  <ListBoxItem id="snake">Snake</ListBoxItem>
</MySelect>
<MySelect
  label="Favorite Animal"
  disabledKeys={['cat', 'kangaroo']}
>
  <ListBoxItem id="red panda">Red Panda</ListBoxItem>
  <ListBoxItem id="cat">Cat</ListBoxItem>
  <ListBoxItem id="dog">Dog</ListBoxItem>
  <ListBoxItem id="aardvark">Aardvark</ListBoxItem>
  <ListBoxItem id="kangaroo">Kangaroo</ListBoxItem>
  <ListBoxItem id="snake">Snake</ListBoxItem>
</MySelect>
<MySelect
  label="Favorite Animal"
  disabledKeys={[
    'cat',
    'kangaroo'
  ]}
>
  <ListBoxItem id="red panda">
    Red Panda
  </ListBoxItem>
  <ListBoxItem id="cat">
    Cat
  </ListBoxItem>
  <ListBoxItem id="dog">
    Dog
  </ListBoxItem>
  <ListBoxItem id="aardvark">
    Aardvark
  </ListBoxItem>
  <ListBoxItem id="kangaroo">
    Kangaroo
  </ListBoxItem>
  <ListBoxItem id="snake">
    Snake
  </ListBoxItem>
</MySelect>

Validation#


Select supports the isRequired prop to ensure the user selects an option, as well as custom client and server-side validation. It can also be integrated with other form libraries. See the Forms guide to learn more.

To display validation errors, add a <FieldError> element as a child of the Select. This allows you to render error messages from all of the above sources with consistent custom styles.

import {Form, FieldError} from 'react-aria-components';

<Form>
  <Select name="animal" isRequired>    <Label>Favorite Animal</Label>
    <Button>
      <SelectValue />
      <span aria-hidden="true"></span>
    </Button>
    <FieldError />    <Popover>
      <ListBox>
        <ListBoxItem>Aardvark</ListBoxItem>
        <ListBoxItem>Cat</ListBoxItem>
        <ListBoxItem>Dog</ListBoxItem>
        <ListBoxItem>Kangaroo</ListBoxItem>
        <ListBoxItem>Panda</ListBoxItem>
        <ListBoxItem>Snake</ListBoxItem>
      </ListBox>
    </Popover>
  </Select>
  <Button type="submit">Submit</Button>
</Form>
import {Form, FieldError} from 'react-aria-components';

<Form>
  <Select name="animal" isRequired>    <Label>Favorite Animal</Label>
    <Button>
      <SelectValue />
      <span aria-hidden="true"></span>
    </Button>
    <FieldError />    <Popover>
      <ListBox>
        <ListBoxItem>Aardvark</ListBoxItem>
        <ListBoxItem>Cat</ListBoxItem>
        <ListBoxItem>Dog</ListBoxItem>
        <ListBoxItem>Kangaroo</ListBoxItem>
        <ListBoxItem>Panda</ListBoxItem>
        <ListBoxItem>Snake</ListBoxItem>
      </ListBox>
    </Popover>
  </Select>
  <Button type="submit">Submit</Button>
</Form>
import {
  FieldError,
  Form
} from 'react-aria-components';

<Form>
  <Select
    name="animal"
    isRequired
  >    <Label>
      Favorite Animal
    </Label>
    <Button>
      <SelectValue />
      <span aria-hidden="true"></span>
    </Button>
    <FieldError />    <Popover>
      <ListBox>
        <ListBoxItem>
          Aardvark
        </ListBoxItem>
        <ListBoxItem>
          Cat
        </ListBoxItem>
        <ListBoxItem>
          Dog
        </ListBoxItem>
        <ListBoxItem>
          Kangaroo
        </ListBoxItem>
        <ListBoxItem>
          Panda
        </ListBoxItem>
        <ListBoxItem>
          Snake
        </ListBoxItem>
      </ListBox>
    </Popover>
  </Select>
  <Button type="submit">
    Submit
  </Button>
</Form>
Show CSS
.react-aria-Select {
  .react-aria-FieldError {
    font-size: 12px;
    color: var(--invalid-color);
  }
}
.react-aria-Select {
  .react-aria-FieldError {
    font-size: 12px;
    color: var(--invalid-color);
  }
}
.react-aria-Select {
  .react-aria-FieldError {
    font-size: 12px;
    color: var(--invalid-color);
  }
}

By default, FieldError displays default validation messages provided by the browser. See Customizing error messages in the Forms guide to learn how to provide your own custom errors.

Description#

The description slot can be used to associate additional help text with a Select.

<Select>
  <Label>Favorite Animal</Label>
  <Button>
    <SelectValue />
    <span aria-hidden="true"></span>
  </Button>
  <Text slot="description">Please select an animal.</Text>  <Popover>
    <ListBox>
      <ListBoxItem>Aardvark</ListBoxItem>
      <ListBoxItem>Cat</ListBoxItem>
      <ListBoxItem>Dog</ListBoxItem>
      <ListBoxItem>Kangaroo</ListBoxItem>
      <ListBoxItem>Panda</ListBoxItem>
      <ListBoxItem>Snake</ListBoxItem>
    </ListBox>
  </Popover>
</Select>
<Select>
  <Label>Favorite Animal</Label>
  <Button>
    <SelectValue />
    <span aria-hidden="true"></span>
  </Button>
  <Text slot="description">Please select an animal.</Text>  <Popover>
    <ListBox>
      <ListBoxItem>Aardvark</ListBoxItem>
      <ListBoxItem>Cat</ListBoxItem>
      <ListBoxItem>Dog</ListBoxItem>
      <ListBoxItem>Kangaroo</ListBoxItem>
      <ListBoxItem>Panda</ListBoxItem>
      <ListBoxItem>Snake</ListBoxItem>
    </ListBox>
  </Popover>
</Select>
<Select>
  <Label>
    Favorite Animal
  </Label>
  <Button>
    <SelectValue />
    <span aria-hidden="true"></span>
  </Button>
  <Text slot="description">
    Please select an
    animal.
  </Text>  <Popover>
    <ListBox>
      <ListBoxItem>
        Aardvark
      </ListBoxItem>
      <ListBoxItem>
        Cat
      </ListBoxItem>
      <ListBoxItem>
        Dog
      </ListBoxItem>
      <ListBoxItem>
        Kangaroo
      </ListBoxItem>
      <ListBoxItem>
        Panda
      </ListBoxItem>
      <ListBoxItem>
        Snake
      </ListBoxItem>
    </ListBox>
  </Popover>
</Select>
Show CSS
.react-aria-Select {
  [slot=description] {
    font-size: 12px;
  }
}
.react-aria-Select {
  [slot=description] {
    font-size: 12px;
  }
}
.react-aria-Select {
  [slot=description] {
    font-size: 12px;
  }
}

Controlled open state#


The open state of the select can be controlled via the defaultOpen and isOpen props

function Example() {
  let [open, setOpen] = React.useState(false);

  return (
    <>
      <p>Select is {open ? 'open' : 'closed'}</p>
      <MySelect label="Choose frequency" isOpen={open} onOpenChange={setOpen}>
        <ListBoxItem id="rarely">Rarely</ListBoxItem>
        <ListBoxItem id="sometimes">Sometimes</ListBoxItem>
        <ListBoxItem id="always">Always</ListBoxItem>
      </MySelect>
    </>
  );
}
function Example() {
  let [open, setOpen] = React.useState(false);

  return (
    <>
      <p>Select is {open ? 'open' : 'closed'}</p>
      <MySelect
        label="Choose frequency"
        isOpen={open}
        onOpenChange={setOpen}
      >
        <ListBoxItem id="rarely">Rarely</ListBoxItem>
        <ListBoxItem id="sometimes">Sometimes</ListBoxItem>
        <ListBoxItem id="always">Always</ListBoxItem>
      </MySelect>
    </>
  );
}
function Example() {
  let [open, setOpen] =
    React.useState(
      false
    );

  return (
    <>
      <p>
        Select is {open
          ? 'open'
          : 'closed'}
      </p>
      <MySelect
        label="Choose frequency"
        isOpen={open}
        onOpenChange={setOpen}
      >
        <ListBoxItem id="rarely">
          Rarely
        </ListBoxItem>
        <ListBoxItem id="sometimes">
          Sometimes
        </ListBoxItem>
        <ListBoxItem id="always">
          Always
        </ListBoxItem>
      </MySelect>
    </>
  );
}

Props#


Select#

NameTypeDefaultDescription
autoCompletestringDescribes the type of autocomplete functionality the input should provide if any. See MDN.
namestringThe name of the input, used when submitting an HTML form.
isOpenbooleanSets the open state of the menu.
defaultOpenbooleanSets the default open state of the menu.
disabledKeysIterable<Key>The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with.
isDisabledbooleanWhether the input is disabled.
isRequiredbooleanWhether user input is required on the input before form submission.
isInvalidbooleanWhether the input value is invalid.
validate( (value: Key )) => ValidationErrortruenullundefined

A function that returns an error message if a given value is invalid. Validation errors are displayed to the user when the form is submitted if validationBehavior="native". For realtime validation, use the isInvalid prop instead.

placeholderstringTemporary text that occupies the text input when it is empty.
selectedKeyKeynullThe currently selected key in the collection (controlled).
defaultSelectedKeyKeyThe initial selected key in the collection (uncontrolled).
autoFocusbooleanWhether the element should receive focus on render.
validationBehavior'native''aria''native'

Whether to use native HTML form validation to prevent form submission when the value is missing or invalid, or mark the field as required or invalid via ARIA.

childrenReactNode( (values: SelectRenderProps )) => ReactNodeThe children of the component. A function may be provided to alter the children based on component state.
classNamestring( (values: SelectRenderProps )) => stringThe CSS className for the element. A function may be provided to compute the class based on component state.
styleCSSProperties( (values: SelectRenderProps )) => CSSPropertiesThe inline style for the element. A function may be provided to compute the style based on component state.
Events
NameTypeDescription
onOpenChange( (isOpen: boolean )) => voidMethod that is called when the open state of the menu changes.
onSelectionChange( (key: Key )) => anyHandler that is called when the selection changes.
onFocus( (e: FocusEvent<Target> )) => voidHandler that is called when the element receives focus.
onBlur( (e: FocusEvent<Target> )) => voidHandler that is called when the element loses focus.
onFocusChange( (isFocused: boolean )) => voidHandler that is called when the element's focus status changes.
onKeyDown( (e: KeyboardEvent )) => voidHandler that is called when a key is pressed.
onKeyUp( (e: KeyboardEvent )) => voidHandler that is called when a key is released.
Layout
NameTypeDescription
slotstringnull

A slot name for the component. Slots allow the component to receive props from a parent component. An explicit null value indicates that the local props completely override all props received from a parent.

Accessibility
NameTypeDescription
idstringThe element's unique identifier. See MDN.
excludeFromTabOrderboolean

Whether to exclude the element from the sequential tab order. If true, the element will not be focusable via the keyboard by tabbing. This should be avoided except in rare scenarios where an alternative means of accessing the element or its functionality via the keyboard is available.

aria-labelstringDefines a string value that labels the current element.
aria-labelledbystringIdentifies the element (or elements) that labels the current element.
aria-describedbystringIdentifies the element (or elements) that describes the object.
aria-detailsstringIdentifies the element (or elements) that provide a detailed, extended description for the object.

Label#

A <Label> accepts all HTML attributes.

Button#

A <Button> accepts its contents as children. Other props such as onPress and isDisabled will be set by the Select.

Show props
NameTypeDefaultDescription
formstring

The <form> element to associate the button with. The value of this attribute must be the id of a <form> in the same document.

formActionstring

The URL that processes the information submitted by the button. Overrides the action attribute of the button's form owner.

formEncTypestringIndicates how to encode the form data that is submitted.
formMethodstringIndicates the HTTP method used to submit the form.
formNoValidatebooleanIndicates that the form is not to be validated when it is submitted.
formTargetstringOverrides the target attribute of the button's form owner.
namestringSubmitted as a pair with the button's value as part of the form data.
valuestringThe value associated with the button's name when it's submitted with the form data.
isDisabledbooleanWhether the button is disabled.
autoFocusbooleanWhether the element should receive focus on render.
type'button''submit''reset''button'The behavior of the button when used in an HTML form.
childrenReactNode( (values: ButtonRenderProps )) => ReactNodeThe children of the component. A function may be provided to alter the children based on component state.
classNamestring( (values: ButtonRenderProps )) => stringThe CSS className for the element. A function may be provided to compute the class based on component state.
styleCSSProperties( (values: ButtonRenderProps )) => CSSPropertiesThe inline style for the element. A function may be provided to compute the style based on component state.
Events
NameTypeDescription
onPress( (e: PressEvent )) => voidHandler that is called when the press is released over the target.
onPressStart( (e: PressEvent )) => voidHandler that is called when a press interaction starts.
onPressEnd( (e: PressEvent )) => void

Handler that is called when a press interaction ends, either over the target or when the pointer leaves the target.

onPressChange( (isPressed: boolean )) => voidHandler that is called when the press state changes.
onPressUp( (e: PressEvent )) => void

Handler that is called when a press is released over the target, regardless of whether it started on the target or not.

onFocus( (e: FocusEvent<Target> )) => voidHandler that is called when the element receives focus.
onBlur( (e: FocusEvent<Target> )) => voidHandler that is called when the element loses focus.
onFocusChange( (isFocused: boolean )) => voidHandler that is called when the element's focus status changes.
onKeyDown( (e: KeyboardEvent )) => voidHandler that is called when a key is pressed.
onKeyUp( (e: KeyboardEvent )) => voidHandler that is called when a key is released.
onHoverStart( (e: HoverEvent )) => voidHandler that is called when a hover interaction starts.
onHoverEnd( (e: HoverEvent )) => voidHandler that is called when a hover interaction ends.
onHoverChange( (isHovering: boolean )) => voidHandler that is called when the hover state changes.
Layout
NameTypeDescription
slotstringnull

A slot name for the component. Slots allow the component to receive props from a parent component. An explicit null value indicates that the local props completely override all props received from a parent.

Accessibility
NameTypeDescription
idstringThe element's unique identifier. See MDN.
excludeFromTabOrderboolean

Whether to exclude the element from the sequential tab order. If true, the element will not be focusable via the keyboard by tabbing. This should be avoided except in rare scenarios where an alternative means of accessing the element or its functionality via the keyboard is available.

aria-expandedboolean'true''false'Indicates whether the element, or another grouping element it controls, is currently expanded or collapsed.
aria-haspopupboolean'menu''listbox''tree''grid''dialog''true''false'Indicates the availability and type of interactive popup element, such as menu or dialog, that can be triggered by an element.
aria-controlsstringIdentifies the element (or elements) whose contents or presence are controlled by the current element.
aria-pressedboolean'true''false''mixed'Indicates the current "pressed" state of toggle buttons.
aria-labelstringDefines a string value that labels the current element.
aria-labelledbystringIdentifies the element (or elements) that labels the current element.
aria-describedbystringIdentifies the element (or elements) that describes the object.
aria-detailsstringIdentifies the element (or elements) that provide a detailed, extended description for the object.

SelectValue#

The <SelectValue> component displays the current value of the select within the <Button>, or a placeholder if no value is selected.

Show props
NameTypeDescription
childrenReactNode( (values: SelectValueRenderProps<object> )) => ReactNodeThe children of the component. A function may be provided to alter the children based on component state.
classNamestring( (values: SelectValueRenderProps<object> )) => stringThe CSS className for the element. A function may be provided to compute the class based on component state.
styleCSSProperties( (values: SelectValueRenderProps<object> )) => CSSPropertiesThe inline style for the element. A function may be provided to compute the style based on component state.

Popover#

A <Popover> is a container to hold the <ListBox> suggestions for a Select. By default, it has a placement of bottom start within a <Select>, but this and other positioning properties may be customized.

Show props
NameTypeDefaultDescription
triggerstring

The name of the component that triggered the popover. This is reflected on the element as the data-trigger attribute, and can be used to provide specific styles for the popover depending on which element triggered it.

triggerRefRefObject<Element>

The ref for the element which the popover positions itself with respect to.

When used within a trigger component such as DialogTrigger, MenuTrigger, Select, etc., this is set automatically. It is only required when used standalone.

isEnteringbooleanWhether the popover is currently performing an entry animation.
isExitingbooleanWhether the popover is currently performing an exit animation.
UNSTABLE_portalContainerElementdocument.bodyThe container element in which the overlay portal will be placed. This may have unknown behavior depending on where it is portalled to.
offsetnumber8

The additional offset applied along the main axis between the element and its anchor element.

placementPlacement'bottom'The placement of the element with respect to its anchor element.
containerPaddingnumber12

The placement padding that should be applied between the element and its surrounding container.

crossOffsetnumber0

The additional offset applied along the cross axis between the element and its anchor element.

shouldFlipbooleantrue

Whether the element should flip its orientation (e.g. top to bottom or left to right) when there is insufficient room for it to render completely.

isNonModalboolean

Whether the popover is non-modal, i.e. elements outside the popover may be interacted with by assistive technologies.

Most popovers should not use this option as it may negatively impact the screen reader experience. Only use with components such as combobox, which are designed to handle this situation carefully.

isKeyboardDismissDisabledbooleanfalse

Whether pressing the escape key to close the popover should be disabled.

Most popovers should not use this option. When set to true, an alternative way to close the popover with a keyboard must be provided.

shouldCloseOnInteractOutside( (element: Element )) => boolean

When user interacts with the argument element outside of the popover ref, return true if onClose should be called. This gives you a chance to filter out interaction with elements that should not dismiss the popover. By default, onClose will always be called on interaction outside the popover ref.

arrowSizenumber0Cross size of the overlay arrow in pixels.
boundaryElementElementdocument.bodyElement that that serves as the positioning boundary.
scrollRefRefObject<Element>overlayRefA ref for the scrollable region within the overlay.
shouldUpdatePositionbooleantrueWhether the overlay should update its position automatically.
arrowBoundaryOffsetnumber0The minimum distance the arrow's edge should be from the edge of the overlay element.
isOpenbooleanWhether the overlay is open by default (controlled).
defaultOpenbooleanWhether the overlay is open by default (uncontrolled).
childrenReactNode( (values: PopoverRenderProps )) => ReactNodeThe children of the component. A function may be provided to alter the children based on component state.
classNamestring( (values: PopoverRenderProps )) => stringThe CSS className for the element. A function may be provided to compute the class based on component state.
styleCSSProperties( (values: PopoverRenderProps )) => CSSPropertiesThe inline style for the element. A function may be provided to compute the style based on component state.
Events
NameTypeDescription
onOpenChange( (isOpen: boolean )) => voidHandler that is called when the overlay's open state changes.
Layout
NameTypeDescription
slotstringnull

A slot name for the component. Slots allow the component to receive props from a parent component. An explicit null value indicates that the local props completely override all props received from a parent.

Sizing
NameTypeDescription
maxHeightnumber

The maxHeight specified for the overlay element. By default, it will take all space up to the current viewport height.

ListBox#

Within a <Select>, most <ListBox> props are set automatically. The <ListBox> defines the options to display in a Select.

Show props
NameTypeDefaultDescription
selectionBehaviorSelectionBehaviorHow multiple selection should behave in the collection.
dragAndDropHooksDragAndDropHooksThe drag and drop hooks returned by useDragAndDrop used to enable drag and drop behavior for the ListBox.
renderEmptyState( (props: ListBoxRenderProps )) => ReactNodeProvides content to display when there are no items in the list.
layout'stack''grid''stack'Whether the items are arranged in a stack or grid.
orientationOrientation'vertical'

The primary orientation of the items. Usually this is the direction that the collection scrolls.

autoFocusbooleanFocusStrategyWhether to auto focus the listbox or an option.
shouldFocusWrapbooleanWhether focus should wrap around when the end/start is reached.
itemsIterable<T>Item objects in the collection.
disabledKeysIterable<Key>The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with.
selectionModeSelectionModeThe type of selection that is allowed in the collection.
disallowEmptySelectionbooleanWhether the collection allows empty selection.
selectedKeys'all'Iterable<Key>The currently selected keys in the collection (controlled).
defaultSelectedKeys'all'Iterable<Key>The initial selected keys in the collection (uncontrolled).
childrenReactNode( (item: object )) => ReactNodeThe contents of the collection.
dependenciesany[]Values that should invalidate the item cache when using dynamic collections.
classNamestring( (values: ListBoxRenderProps )) => stringThe CSS className for the element. A function may be provided to compute the class based on component state.
styleCSSProperties( (values: ListBoxRenderProps )) => CSSPropertiesThe inline style for the element. A function may be provided to compute the style based on component state.
Events
NameTypeDescription
onAction( (key: Key )) => void

Handler that is called when a user performs an action on an item. The exact user event depends on the collection's selectionBehavior prop and the interaction modality.

onSelectionChange( (keys: Selection )) => anyHandler that is called when the selection changes.
onFocus( (e: FocusEvent<Target> )) => voidHandler that is called when the element receives focus.
onBlur( (e: FocusEvent<Target> )) => voidHandler that is called when the element loses focus.
onFocusChange( (isFocused: boolean )) => voidHandler that is called when the element's focus status changes.
onScroll( (e: UIEvent<Element> )) => voidHandler that is called when a user scrolls. See MDN.
Layout
NameTypeDescription
slotstringnull

A slot name for the component. Slots allow the component to receive props from a parent component. An explicit null value indicates that the local props completely override all props received from a parent.

Accessibility
NameTypeDescription
idstringThe element's unique identifier. See MDN.
aria-labelstringDefines a string value that labels the current element.
aria-labelledbystringIdentifies the element (or elements) that labels the current element.
aria-describedbystringIdentifies the element (or elements) that describes the object.
aria-detailsstringIdentifies the element (or elements) that provide a detailed, extended description for the object.

Section#

A <Section> defines the child items for a section within a <ListBox>. It may also contain an optional <Header> element. If there is no header, then an aria-label must be provided to identify the section to assistive technologies.

Show props
NameTypeDescription
idKeyThe unique id of the section.
valueobjectThe object value that this section represents. When using dynamic collections, this is set automatically.
childrenReactNode( (item: object )) => ReactElementStatic child items or a function to render children.
dependenciesany[]Values that should invalidate the item cache when using dynamic collections.
itemsIterable<T>Item objects in the section.
classNamestringThe CSS className for the element.
styleCSSPropertiesThe inline style for the element.
Accessibility
NameTypeDescription
aria-labelstringAn accessibility label for the section.

A <Header> defines the title for a <Section>. It accepts all DOM attributes.

ListBoxItem#

A <ListBoxItem> defines a single option within a <ListBox>. If the children are not plain text, then the textValue prop must also be set to a plain text representation, which will be used for autocomplete in the Select.

Show props
NameTypeDescription
idKeyThe unique id of the item.
valueobjectThe object value that this item represents. When using dynamic collections, this is set automatically.
textValuestringA string representation of the item's contents, used for features like typeahead.
childrenReactNode( (values: ListBoxItemRenderProps )) => ReactNodeThe children of the component. A function may be provided to alter the children based on component state.
classNamestring( (values: ListBoxItemRenderProps )) => stringThe CSS className for the element. A function may be provided to compute the class based on component state.
styleCSSProperties( (values: ListBoxItemRenderProps )) => CSSPropertiesThe inline style for the element. A function may be provided to compute the style based on component state.
hrefstringA URL to link to. See MDN.
targetHTMLAttributeAnchorTargetThe target window for the link. See MDN.
relstringThe relationship between the linked resource and the current page. See MDN.
downloadbooleanstringCauses the browser to download the linked URL. A string may be provided to suggest a file name. See MDN.
pingstringA space-separated list of URLs to ping when the link is followed. See MDN.
referrerPolicyHTMLAttributeReferrerPolicyHow much of the referrer to send when following the link. See MDN.
Accessibility
NameTypeDescription
aria-labelstringAn accessibility label for this item.

FieldError#

A <FieldError> displays validation errors.

Show props
NameTypeDescription
childrenReactNode( (values: FieldErrorRenderProps )) => ReactNodeThe children of the component. A function may be provided to alter the children based on component state.
classNamestring( (values: FieldErrorRenderProps )) => stringThe CSS className for the element. A function may be provided to compute the class based on component state.
styleCSSProperties( (values: FieldErrorRenderProps )) => CSSPropertiesThe inline style for the element. A function may be provided to compute the style based on component state.

Styling#


React Aria components can be styled in many ways, including using CSS classes, inline styles, utility classes (e.g. Tailwind), CSS-in-JS (e.g. Styled Components), etc. By default, all components include a builtin className attribute which can be targeted using CSS selectors. These follow the react-aria-ComponentName naming convention.

.react-aria-Select {
  /* ... */
}
.react-aria-Select {
  /* ... */
}
.react-aria-Select {
  /* ... */
}

A custom className can also be specified on any component. This overrides the default className provided by React Aria with your own.

<Select className="my-select">
  {/* ... */}
</Select>
<Select className="my-select">
  {/* ... */}
</Select>
<Select className="my-select">
  {/* ... */}
</Select>

In addition, some components support multiple UI states (e.g. pressed, hovered, etc.). React Aria components expose states using data attributes, which you can target in CSS selectors. For example:

.react-aria-ListBoxItem[data-selected] {
  /* ... */
}

.react-aria-ListBoxItem[data-focused] {
  /* ... */
}
.react-aria-ListBoxItem[data-selected] {
  /* ... */
}

.react-aria-ListBoxItem[data-focused] {
  /* ... */
}
.react-aria-ListBoxItem[data-selected] {
  /* ... */
}

.react-aria-ListBoxItem[data-focused] {
  /* ... */
}

The className and style props also accept functions which receive states for styling. This lets you dynamically determine the classes or styles to apply, which is useful when using utility CSS libraries like Tailwind.

<ListBoxItem
  className={({ isSelected }) => isSelected ? 'bg-blue-400' : 'bg-gray-100'}
>
  Item
</ListBoxItem>
<ListBoxItem
  className={({ isSelected }) =>
    isSelected ? 'bg-blue-400' : 'bg-gray-100'}
>
  Item
</ListBoxItem>
<ListBoxItem
  className={(
    { isSelected }
  ) =>
    isSelected
      ? 'bg-blue-400'
      : 'bg-gray-100'}
>
  Item
</ListBoxItem>

Render props may also be used as children to alter what elements are rendered based on the current state. For example, you could render a checkmark icon when an item is selected.

<ListBoxItem>
  {({isSelected}) => (
    <>
      {isSelected && <CheckmarkIcon />}
      Item
    </>
  )}
</ListBoxItem>
<ListBoxItem>
  {({isSelected}) => (
    <>
      {isSelected && <CheckmarkIcon />}
      Item
    </>
  )}
</ListBoxItem>
<ListBoxItem>
  {(
    { isSelected }
  ) => (
    <>
      {isSelected && (
        <CheckmarkIcon />
      )}
      Item
    </>
  )}
</ListBoxItem>

The states and selectors for each component used in a Select are documented below.

Select#

A Select can be targeted with the .react-aria-Select CSS selector, or by overriding with a custom className. It supports the following states:

NameCSS SelectorDescription
isFocused[data-focused]Whether the select is focused, either via a mouse or keyboard.
isFocusVisible[data-focus-visible]Whether the select is keyboard focused.
isDisabled[data-disabled]Whether the select is disabled.
isOpen[data-open]Whether the select is currently open.
isInvalid[data-invalid]Whether the select is invalid.
isRequired[data-required]Whether the select is required.

Label#

A Label can be targeted with the .react-aria-Label CSS selector, or by overriding with a custom className.

Button#

A Button can be targeted with the .react-aria-Button CSS selector, or by overriding with a custom className. It supports the following states:

NameCSS SelectorDescription
isHovered[data-hovered]Whether the button is currently hovered with a mouse.
isPressed[data-pressed]Whether the button is currently in a pressed state.
isFocused[data-focused]Whether the button is focused, either via a mouse or keyboard.
isFocusVisible[data-focus-visible]Whether the button is keyboard focused.
isDisabled[data-disabled]Whether the button is disabled.

SelectValue#

A SelectValue can be targed with the .react-aria-SelectValue CSS selector, or by overriding with a custom className. It supports the following states and render props:

NameCSS SelectorDescription
isPlaceholder[data-placeholder]Whether the value is a placeholder.
selectedItemThe object value of the currently selected item.
selectedTextThe textValue of the currently selected item.

Popover#

The Popover component can be targeted with the .react-aria-Popover CSS selector, or by overriding with a custom className. Note that it renders in a React Portal, so it will not appear as a descendant of the Select in the DOM. It supports the following states and render props:

NameCSS SelectorDescription
trigger[data-trigger="..."]The name of the component that triggered the popover, e.g. "DialogTrigger" or "ComboBox".
placement[data-placement="left | right | top | bottom"]The placement of the popover relative to the trigger.
isEntering[data-entering]Whether the popover is currently entering. Use this to apply animations.
isExiting[data-exiting]Whether the popover is currently exiting. Use this to apply animations.

Within a Select, the popover will have the data-trigger="Select" attribute, which can be used to define select-specific styles. In addition, the --trigger-width CSS custom property will be set on the popover, which you can use to make the popover match the width of the select button.

.react-aria-Popover[data-trigger=Select] {
  width: var(--trigger-width);
}
.react-aria-Popover[data-trigger=Select] {
  width: var(--trigger-width);
}
.react-aria-Popover[data-trigger=Select] {
  width: var(--trigger-width);
}

ListBox#

A ListBox can be targeted with the .react-aria-ListBox CSS selector, or by overriding with a custom className.

Section#

A Section can be targeted with the .react-aria-Section CSS selector, or by overriding with a custom className. See sections for examples.

Header#

A Header within a Section can be targeted with the .react-aria-Header CSS selector, or by overriding with a custom className. See sections for examples.

ListBoxItem#

A ListBoxItem can be targeted with the .react-aria-ListBoxItem CSS selector, or by overriding with a custom className. It supports the following states and render props:

NameCSS SelectorDescription
isHovered[data-hovered]Whether the item is currently hovered with a mouse.
isPressed[data-pressed]Whether the item is currently in a pressed state.
isSelected[data-selected]Whether the item is currently selected.
isFocused[data-focused]Whether the item is currently focused.
isFocusVisible[data-focus-visible]Whether the item is currently keyboard focused.
isDisabled[data-disabled]

Whether the item is non-interactive, i.e. both selection and actions are disabled and the item may not be focused. Dependent on disabledKeys and disabledBehavior.

selectionMode[data-selection-mode="single | multiple"]The type of selection that is allowed in the collection.
selectionBehaviorThe selection behavior for the collection.

Items also support two slots: a label, and a description. When provided using the <Text> element, the item will have aria-labelledby and aria-describedby attributes pointing to these slots, improving screen reader announcement. See complex items for an example.

Note that items may not contain interactive children such as buttons, as screen readers will not be able to access them.

Text#

The help text elements within a Select can be targeted with the [slot=description] and [slot=errorMessage] CSS selectors, or by adding a custom className.

FieldError#

A FieldError can be targeted with the .react-aria-FieldError CSS selector, or by overriding with a custom className. It supports the following render props:

NameDescription
isInvalidWhether the input value is invalid.
validationErrorsThe current error messages for the input if it is invalid, otherwise an empty array.
validationDetailsThe native validation details for the input.

Advanced customization#


Composition#

If you need to customize one of the components within a Select, such as Button or ListBox, in many cases you can create a wrapper component. This lets you customize the props passed to the component.

function MyListBox(props) {
  return <ListBox {...props} className="my-listbox" />
}
function MyListBox(props) {
  return <ListBox {...props} className="my-listbox" />
}
function MyListBox(
  props
) {
  return (
    <ListBox
      {...props}
      className="my-listbox"
    />
  );
}

Contexts#

All React Aria Components export a corresponding context that can be used to send props to them from a parent element. This enables you to build your own compositional APIs similar to those found in React Aria Components itself. You can send any prop or ref via context that you could pass to the corresponding component. The local props and ref on the component are merged with the ones passed via context, with the local props taking precedence (following the rules documented in mergeProps).

ComponentContextPropsRef
SelectSelectContextSelectPropsHTMLDivElement

This example shows a FieldGroup component that renders a group of selects with a title. The entire group can be marked as disabled via the isDisabled prop, which is passed to all child selects via the SelectContext provider.

import {SelectContext} from 'react-aria-components';

interface FieldGroupProps {
  title?: string,
  children?: React.ReactNode,
  isDisabled?: boolean
}

function FieldGroup({title, children, isDisabled}: FieldGroupProps) {
  return (
    <fieldset>
      <legend>{title}</legend>
      <SelectContext.Provider value={{isDisabled}}>        {children}
      </SelectContext.Provider>
    </fieldset>
  );
}

<FieldGroup title="Filters" isDisabled>
  <MySelect label="Status" defaultSelectedKey="published">
    <ListBoxItem id="draft">Draft</ListBoxItem>
    <ListBoxItem id="published">Published</ListBoxItem>
    <ListBoxItem id="deleted">Deleted</ListBoxItem>
  </MySelect>
  <MySelect label="Author" defaultSelectedKey="emma">
    <ListBoxItem id="john">John</ListBoxItem>
    <ListBoxItem id="emma">Emma</ListBoxItem>
    <ListBoxItem id="tim">Tim</ListBoxItem>
  </MySelect>
</FieldGroup>
import {SelectContext} from 'react-aria-components';

interface FieldGroupProps {
  title?: string;
  children?: React.ReactNode;
  isDisabled?: boolean;
}

function FieldGroup(
  { title, children, isDisabled }: FieldGroupProps
) {
  return (
    <fieldset>
      <legend>{title}</legend>
      <SelectContext.Provider value={{ isDisabled }}>        {children}
      </SelectContext.Provider>
    </fieldset>
  );
}

<FieldGroup title="Filters" isDisabled>
  <MySelect label="Status" defaultSelectedKey="published">
    <ListBoxItem id="draft">Draft</ListBoxItem>
    <ListBoxItem id="published">Published</ListBoxItem>
    <ListBoxItem id="deleted">Deleted</ListBoxItem>
  </MySelect>
  <MySelect label="Author" defaultSelectedKey="emma">
    <ListBoxItem id="john">John</ListBoxItem>
    <ListBoxItem id="emma">Emma</ListBoxItem>
    <ListBoxItem id="tim">Tim</ListBoxItem>
  </MySelect>
</FieldGroup>
import {SelectContext} from 'react-aria-components';

interface FieldGroupProps {
  title?: string;
  children?:
    React.ReactNode;
  isDisabled?: boolean;
}

function FieldGroup(
  {
    title,
    children,
    isDisabled
  }: FieldGroupProps
) {
  return (
    <fieldset>
      <legend>
        {title}
      </legend>
      <SelectContext.Provider
        value={{
          isDisabled
        }}
      >        {children}
      </SelectContext.Provider>
    </fieldset>
  );
}

<FieldGroup
  title="Filters"
  isDisabled
>
  <MySelect
    label="Status"
    defaultSelectedKey="published"
  >
    <ListBoxItem id="draft">
      Draft
    </ListBoxItem>
    <ListBoxItem id="published">
      Published
    </ListBoxItem>
    <ListBoxItem id="deleted">
      Deleted
    </ListBoxItem>
  </MySelect>
  <MySelect
    label="Author"
    defaultSelectedKey="emma"
  >
    <ListBoxItem id="john">
      John
    </ListBoxItem>
    <ListBoxItem id="emma">
      Emma
    </ListBoxItem>
    <ListBoxItem id="tim">
      Tim
    </ListBoxItem>
  </MySelect>
</FieldGroup>
Show CSS
fieldset {
  padding: 1.5em;
  width: fit-content;
}
fieldset {
  padding: 1.5em;
  width: fit-content;
}
fieldset {
  padding: 1.5em;
  width: fit-content;
}

Custom children#

Select passes props to its child components, such as the label and popover, via their associated contexts. These contexts are exported so you can also consume them in your own custom components. This enables you to reuse existing components from your app or component library together with React Aria Components.

ComponentContextPropsRef
LabelLabelContextLabelPropsHTMLLabelElement
ButtonButtonContextButtonPropsHTMLButtonElement
PopoverPopoverContextPopoverPropsHTMLElement
ListBoxListBoxContextListBoxPropsHTMLDivElement
TextTextContextTextPropsHTMLElement

This example consumes from LabelContext in an existing styled label component to make it compatible with React Aria Components. The useContextProps hook merges the local props and ref with the ones provided via context by Select.

import type {LabelProps} from 'react-aria-components';
import {LabelContext, useContextProps} from 'react-aria-components';

const MyCustomLabel = React.forwardRef(
  (props: LabelProps, ref: React.ForwardedRef<HTMLLabelElement>) => {
    // Merge the local props and ref with the ones provided via context.
    [props, ref] = useContextProps(props, ref, LabelContext);
    // ... your existing Label component
    return <label {...props} ref={ref} />;
  }
);
import type {LabelProps} from 'react-aria-components';
import {
  LabelContext,
  useContextProps
} from 'react-aria-components';

const MyCustomLabel = React.forwardRef(
  (
    props: LabelProps,
    ref: React.ForwardedRef<HTMLLabelElement>
  ) => {
    // Merge the local props and ref with the ones provided via context.
    [props, ref] = useContextProps(
      props,
      ref,
      LabelContext
    );
    // ... your existing Label component
    return <label {...props} ref={ref} />;
  }
);
import type {LabelProps} from 'react-aria-components';
import {
  LabelContext,
  useContextProps
} from 'react-aria-components';

const MyCustomLabel =
  React.forwardRef(
    (
      props: LabelProps,
      ref:
        React.ForwardedRef<
          HTMLLabelElement
        >
    ) => {
      // Merge the local props and ref with the ones provided via context.
      [props, ref] =
        useContextProps(
          props,
          ref,
          LabelContext
        );
      // ... your existing Label component
      return (
        <label
          {...props}
          ref={ref}
        />
      );
    }
  );

Now you can use MyCustomLabel within a Select, in place of the builtin React Aria Components Label.

<Select>
  <MyCustomLabel>Name</MyCustomLabel>  {/* ... */}
</Select>
<Select>
  <MyCustomLabel>Name</MyCustomLabel>  {/* ... */}
</Select>
<Select>
  <MyCustomLabel>
    Name
  </MyCustomLabel>  {/* ... */}
</Select>

State#

Select provides an SelectState object to its children via SelectStateContext. This can be used to access and manipulate the select's state.

This example shows a SelectClearButton component that can be placed within a Select to allow the user to clear the selected item.

import {SelectStateContext} from 'react-aria-components';

function SelectClearButton() {
  let state = React.useContext(SelectStateContext);  return (
    <Button
      // Don't inherit behavior from Select.
      slot={null}
      style={{fontSize: 'small', marginTop: 6, padding: 4}}
      onPress={() => state?.setSelectedKey(null)}>
      Clear
    </Button>
  );
}

<Select>
  <Label>Favorite Animal</Label>
  <Button>
    <SelectValue />
    <span aria-hidden="true"></span>
  </Button>
  <SelectClearButton />  <Popover>
    <ListBox>
      <ListBoxItem>Cat</ListBoxItem>
      <ListBoxItem>Dog</ListBoxItem>
      <ListBoxItem>Kangaroo</ListBoxItem>
    </ListBox>
  </Popover>
</Select>
import {SelectStateContext} from 'react-aria-components';

function SelectClearButton() {
  let state = React.useContext(SelectStateContext);  return (
    <Button
      // Don't inherit behavior from Select.
      slot={null}
      style={{fontSize: 'small', marginTop: 6, padding: 4}}
      onPress={() => state?.setSelectedKey(null)}>
      Clear
    </Button>
  );
}

<Select>
  <Label>Favorite Animal</Label>
  <Button>
    <SelectValue />
    <span aria-hidden="true"></span>
  </Button>
  <SelectClearButton />  <Popover>
    <ListBox>
      <ListBoxItem>Cat</ListBoxItem>
      <ListBoxItem>Dog</ListBoxItem>
      <ListBoxItem>Kangaroo</ListBoxItem>
    </ListBox>
  </Popover>
</Select>
import {SelectStateContext} from 'react-aria-components';

function SelectClearButton() {
  let state = React
    .useContext(
      SelectStateContext
    );  return (
    <Button
      // Don't inherit behavior from Select.
      slot={null}
      style={{
        fontSize:
          'small',
        marginTop: 6,
        padding: 4
      }}
      onPress={() =>
        state
          ?.setSelectedKey(
            null
          )}
    >
      Clear
    </Button>
  );
}

<Select>
  <Label>
    Favorite Animal
  </Label>
  <Button>
    <SelectValue />
    <span aria-hidden="true"></span>
  </Button>
  <SelectClearButton />  <Popover>
    <ListBox>
      <ListBoxItem>
        Cat
      </ListBoxItem>
      <ListBoxItem>
        Dog
      </ListBoxItem>
      <ListBoxItem>
        Kangaroo
      </ListBoxItem>
    </ListBox>
  </Popover>
</Select>

Hooks#

If you need to customize things even further, such as accessing internal state, intercepting events, or customizing the DOM structure, you can drop down to the lower level Hook-based API. See useSelect for more details.

Accessibility#


False positives#

Select may trigger a known accessibility false positive from automated accessibility testing tools. This is because we include a visually hidden select element alongside the Select to specifically aid with browser form autocomplete and hide it from screen readers via aria-hidden since users don't need to interact with the hidden select. We manage focus internally so that focusing this hidden select will always shift focus to the Select's trigger button instead. Automated accessibility testing tools have no way of knowing that we manage the focus in this way and thus throw this false positive.

To facilitate the suppression of this false positive, the data-a11y-ignore="aria-hidden-focus" data attribute is automatically applied to the problematic element and references the relevant AXE rule. Please use this data attribute to target the problematic element and exclude it from your automated accessibility tests as shown here.