Beta Preview

Picker

Pickers allow users to choose a single option from a collapsible list of options when space is limited.

Ice cream flavor
 
selectionMode 
size 
labelPosition 
 
contextualHelp 
isQuiet 
isDisabled 
import {Picker, PickerItem} from '@react-spectrum/s2';

<Picker label="Ice cream flavor">
  <PickerItem>Chocolate</PickerItem>
  <PickerItem>Mint</PickerItem>
  <PickerItem>Strawberry</PickerItem>
  <PickerItem>Vanilla</PickerItem>
  <PickerItem>Chocolate Chip Cookie Dough</PickerItem>
</Picker>

Content

Picker 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.

Animals
import {Picker, PickerItem} from '@react-spectrum/s2';

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 (
    <Picker label="Animals" items={options}>
      {(item) => <PickerItem>{item.name}</PickerItem>}
    </Picker>
  );
}

Slots

PickerItem supports icons, avatars, and label and description text slots.

Permissions
Share
import {Avatar, Picker, PickerItem, Text} from '@react-spectrum/s2';
import {style} from '@react-spectrum/s2/style' with {type: 'macro'};
import Comment from '@react-spectrum/s2/icons/Comment';
import Edit from '@react-spectrum/s2/icons/Edit';
import UserSettings from '@react-spectrum/s2/icons/UserSettings';

const users = Array.from({length: 5}, (_, i) => ({
  id: `user${i + 1}`,
  name: `User ${i + 1}`,
  email: `user${i + 1}@example.com`,
  avatar: 'https://i.imgur.com/kJOwAdv.png'
}));

<div className={style({display: 'flex', gap: 12, flexWrap: 'wrap'})}>
  <Picker label="Permissions" defaultValue="read">
    <PickerItem id="read" textValue="Read">
      <Comment />
      <Text slot="label">Read</Text>
      <Text slot="description">Comment only</Text>
    </PickerItem>
    <PickerItem id="write" textValue="Write">
      <Edit />
      <Text slot="label">Write</Text>
      <Text slot="description">Read and write only</Text>
    </PickerItem>
    <PickerItem id="admin" textValue="Admin">
      <UserSettings />
      <Text slot="label">Admin</Text>
      <Text slot="description">Full access</Text>
    </PickerItem>
  </Picker>
  <Picker label="Share" items={users} defaultValue="user1">
    {(item) => (
      <PickerItem id={item.id} textValue={item.name}>
        <Avatar slot="avatar" src={item.avatar} />
        <Text slot="label">{item.name}</Text>
        <Text slot="description">{item.email}</Text>
      </PickerItem>
    )}
  </Picker>
</div>

Sections

Use the <PickerSection> component to group options. A <Header> element, with a <Heading> and optional description slot can be included to label a section. Sections without a header must have an aria-label.

Ice cream flavor
import {Picker, PickerSection, PickerItem, Header, Heading, Text} from '@react-spectrum/s2';

<Picker label="Ice cream flavor">
  <PickerSection>
    <Header>
      <Heading>Neopolitan flavors</Heading>
      <Text slot="description">These flavors are common</Text>
    </Header>
    <PickerItem>Chocolate</PickerItem>
    <PickerItem>Strawberry</PickerItem>
    <PickerItem>Vanilla</PickerItem>
  </PickerSection>
  <PickerSection>
    <Header>
      <Heading>Others</Heading>
      <Text slot="description">These flavors are uncommon</Text>
    </Header>
    <PickerItem>Matcha</PickerItem>
    <PickerItem>Ube</PickerItem>
    <PickerItem>Prickly pear</PickerItem>
  </PickerSection>
</Picker>

Asynchronous loading

Use the loadingState and onLoadMore props to enable async loading and infinite scrolling.

import {Picker, PickerItem} from '@react-spectrum/s2';
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 (
    <Picker
      aria-label="Pokemon"
      items={list.items}
      loadingState={list.loadingState}
      onLoadMore={list.loadMore}>
      {(item) => <PickerItem id={item.name}>{item.name}</PickerItem>}
    </Picker>
  );
}

Use the href prop on a <PickerItem> to create a link. See the client side routing guide to learn how to integrate with your framework. Link items in a Picker are not selectable.

Project
import {Picker, PickerItem} from '@react-spectrum/s2';

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

Value

Use the defaultValue or value prop to set the selected item. The value corresponds to the id prop of an item. When selectionMode="multiple", value and onChange accept an array. Items can be disabled with the isDisabled prop.

Pick an animal
Current selection: "bison"
selectionMode 
import {Picker, PickerItem} from '@react-spectrum/s2';
import {style} from '@react-spectrum/s2/style' with {type: 'macro'};
import {useState} from 'react';

function Example(props) {
  let [animal, setAnimal] = useState("bison");

  return (
    <div>
      <Picker
        {...props}
        label="Pick an animal"
        value={animal}
        onChange={setAnimal}
      >
        <PickerItem id="koala">Koala</PickerItem>
        <PickerItem id="kangaroo">Kangaroo</PickerItem>
        <PickerItem id="platypus" isDisabled>Platypus</PickerItem>
        <PickerItem id="eagle">Bald Eagle</PickerItem>
        <PickerItem id="bison">Bison</PickerItem>
        <PickerItem id="skunk">Skunk</PickerItem>
      </Picker>
      <pre className={style({font: 'body'})}>Current selection: {JSON.stringify(animal)}</pre>
    </div>
  );
}

Forms

Use the name prop to submit the id of the selected item to the server. Set the isRequired prop to validate that the user selects an option, or implement custom client or server-side validation. See the Forms guide to learn more.

Animal 
Please select an animal.
isRequired 
necessityIndicator 
import {Picker, PickerItem, Form, Button} from '@react-spectrum/s2';

function Example(props) {
  return (
    <Form>
      <Picker
        {...props}
        label="Animal"
        name="animal"
        isRequired
        description="Please select an animal.">
        <PickerItem id="aardvark">Aardvark</PickerItem>
        <PickerItem id="cat">Cat</PickerItem>
        <PickerItem id="dog">Dog</PickerItem>
        <PickerItem id="kangaroo">Kangaroo</PickerItem>
        <PickerItem id="panda">Panda</PickerItem>
        <PickerItem id="snake">Snake</PickerItem>
      </Picker>
      <Button type="submit">Submit</Button>
    </Form>
  );
}

Popover options

The open state of the Picker can be controlled via the defaultOpen and isOpen props. The align, direction, shouldFlip and menuWidth props control the behavior of the popover.

Select is closed

Choose frequency
align 
direction 
shouldFlip 
 
import {Picker, PickerItem} from '@react-spectrum/s2';
import {useState} from 'react';

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

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

API

<Picker>
  <PickerItem>
    <Icon />
    <Text slot="label" />
    <Text slot="description" />
  </PickerItem>
  <PickerSection>
    <Header>
      <Heading />
      <Text slot="description" />
    </Header>
    <PickerItem />
  </PickerSection>
</Picker>

Picker

NameTypeDefault
placeholderstringDefault: 'Select an item' (localized)
Temporary text that occupies the select when it is empty.
allowsEmptyCollectionbooleanDefault:
Whether the select should be allowed to be open when the collection is empty.
isDisabledbooleanDefault:
Whether the input is disabled.
size'S''M''L''XL'Default: 'M'
The size of the Picker.
stylesDefault:
Spectrum-defined styles, returned by the style() macro.
childrenReactNode(item: object) => ReactNodeDefault:
The contents of the collection.
itemsIterable<T>Default:
Item objects in the collection.
loadingStateDefault:
The current loading state of the Picker.
onLoadMore() => anyDefault:
Handler that is called when more items should be loaded, e.g. while scrolling near the bottom.
dependenciesReadonlyArray<any>Default:
Values that should invalidate the item cache when using dynamic collections.
selectionModeDefault: 'single'
Whether single or multiple selection is enabled.
disabledKeysIterable<Key>Default:
The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with.
value<>Default:
The current value (controlled).
defaultValue<>Default:
The default value (uncontrolled).
onChange(value: T) => voidDefault:
Handler that is called when the value changes.

PickerItem

NameType
childrenReactNode
idKey
The unique id of the item.
valueT
The object value that this item represents. When using dynamic collections, this is set automatically.
textValuestring
A string representation of the item's contents, used for features like typeahead.
isDisabledboolean
Whether the item is disabled.
styles
Spectrum-defined styles, returned by the style() macro.

PickerSection

NameType
idKey
The unique id of the section.
valueT
The object value that this section represents. When using dynamic collections, this is set automatically.
childrenReactNode(item: T) => ReactElement
Static child items or a function to render children.
itemsIterable<T>
Item objects in the section.
dependenciesReadonlyArray<any>
Values that should invalidate the item cache when using dynamic collections.

Testing

Test utils

@react-spectrum/test-utils offers common picker interaction utilities which you may find helpful when writing tests. To install, simply add it to your dev dependencies via your preferred package manager.

yarn add @react-spectrum/test-utils --dev

Once installed, you can access the User that @react-spectrum/test-utils provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate the Picker tester in your test cases. This gives you access to Picker specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. The example test case below shows how you might go about setting up the Picker tester, use it to simulate option selection, and verify the picker's state after each interaction.

// Picker.test.ts
import {render} from '@testing-library/react';
import {User} from '@react-spectrum/test-utils';

let testUtilUser = new User({interactionType: 'mouse'});
// ...

it('Picker can select an option via keyboard', async function () {
  // Render your test component/app and initialize the select tester
  let {getByTestId} = render(
    <Picker data-testid="test-select">
    ...
    </Picker>
  );
  let selectTester = testUtilUser.createTester('Select', {root: getByTestId('test-select'), interactionType: 'keyboard'});
  let trigger = selectTester.trigger;
  expect(trigger).toHaveTextContent('Select an item');

  await selectTester.selectOption({option: 'Cat'});
  expect(trigger).toHaveTextContent('Cat');
});

See below for the full definition of the User and the Picker tester.

Properties

NameTypeDefault
advanceTimer['advanceTimer']Default:
A function used by the test utils to advance timers during interactions. Required for certain aria patterns (e.g. table).
interactionType['interactionType']Default: mouse
The interaction type (mouse, touch, keyboard) that the test util user will use when interacting with a component. This can be overridden at the aria pattern util level if needed.

Methods

constructor(opts: ): void
createTester<T extends >(patternName: T, opts: <T>): <T>
Creates an aria pattern tester, inheriting the options provided to the original user.

Properties

NameType
sectionsHTMLElement[]
Returns the select's sections if present.
listboxHTMLElementnull
Returns the select's listbox if present.
triggerHTMLElement
Returns the select's trigger.

Methods

constructor(opts: ): void
setInteractionType(type: ['interactionType']): void
Set the interaction type used by the select tester.
open(opts: ): Promise<void>
Opens the select. Defaults to using the interaction type set on the select tester.
close(): Promise<void>
Closes the select.
findOption(opts: {
optionIndexOrText: numberstring
}): HTMLElement
Returns a option matching the specified index or text content.
selectOption(opts: ): Promise<void>
Selects the desired select option. Defaults to using the interaction type set on the select tester. If necessary, will open the select dropdown beforehand. The desired option can be targeted via the option's node, the option's text, or the option's index.
options(opts: {
element?: HTMLElement
}): HTMLElement[]
Returns the select's options if present. Can be filtered to a subsection of the listbox if provided via element.

Testing FAQ