Beta Preview

Selection

Many collection components support selecting items by clicking or tapping them, or by using the keyboard. This page discusses how to handle selection events, how to control selection programmatically, and the data structures used to represent a selection.

Multiple selection

Most collection components support item selection, which is handled by the onSelectionChange event. Use the selectedKeys prop to control the selected items programmatically, or defaultSelectedKeys for uncontrolled behavior.

Selection is represented by a Set containing the id of each selected item. You can also pass any iterable collection (e.g. an array) to the selectedKeys and defaultSelectedKeys props, but the onSelectionChange event will always pass back a Set.

selectedKeys: lion

import {CardView, AssetCard, CardPreview, Image, Content, Text, type Selection} from '@react-spectrum/s2';
import {style} from '@react-spectrum/s2/style' with {type: 'macro'};
import {useState} from 'react';

function Example() {
  let [selectedKeys, setSelectedKeys] = useState<Selection>(new Set(['lion']));

  return (
    <div>
      <CardView
        aria-label="Nature photos"
        selectionMode="multiple"
        selectedKeys={selectedKeys}
        onSelectionChange={setSelectedKeys}
        styles={style({width: 360, height: 520})}
      >
        <AssetCard id="desert" textValue="Desert Sunset">
          <CardPreview>
            <Image src="https://images.unsplash.com/photo-1705034598432-1694e203cdf3?q=80&w=600&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" width={600} height={400} />
          </CardPreview>
          <Content>
            <Text slot="title">Desert Sunset</Text>
            <Text slot="description">PNG • 2/3/2024</Text>
          </Content>
        </AssetCard>
        <AssetCard id="hiking" textValue="Hiking Trail">
          <CardPreview>
            <Image src="https://images.unsplash.com/photo-1722233987129-61dc344db8b6?q=80&w=600&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" width={600} height={900} />
          </CardPreview>
          <Content>
            <Text slot="title">Hiking Trail</Text>
            <Text slot="description">JPEG • 1/10/2022</Text>
          </Content>
        </AssetCard>
        <AssetCard id="lion" textValue="Lion">
          <CardPreview>
            <Image src="https://images.unsplash.com/photo-1629812456605-4a044aa38fbc?q=80&w=600&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" width={600} height={899} />
          </CardPreview>
          <Content>
            <Text slot="title">Lion</Text>
            <Text slot="description">JPEG • 8/28/2021</Text>
          </Content>
        </AssetCard>
        <AssetCard id="mountain" textValue="Mountain Sunrise">
          <CardPreview>
            <Image src="https://images.unsplash.com/photo-1722172118908-1a97c312ce8c?q=80&w=600&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" width={600} height={900} />
          </CardPreview>
          <Content>
            <Text slot="title">Mountain Sunrise</Text>
            <Text slot="description">PNG • 3/15/2015</Text>
          </Content>
        </AssetCard>
      </CardView>
      <p className={style({font: 'body'})}>selectedKeys: {selectedKeys === 'all' ? 'all' : [...selectedKeys].join(', ')}</p>
    </div>
  );
}

Select all

Some components support a checkbox to select all items in the collection, or a keyboard shortcut like ⌘ Cmd + A. This represents a selection of all items in the collection, regardless of whether or not all items have been loaded from the network. For example, when using a component with infinite scrolling support, the user will be unaware that all items are not yet loaded. For this reason, it makes sense for select all to represent all items, not just the loaded ones.

When a select all event occurs, onSelectionChange is called with the string "all" rather than a set of selected keys. selectedKeys and defaultSelectedKeys can also be set to "all" to programmatically select all items. The application must adjust its handling of bulk actions in this case to apply to the entire collection rather than only the keys available to it locally.

Name

selectedKeys:

import {TableView, TableHeader, Column, TableBody, Row, Cell} from '@react-spectrum/s2';
import {style} from '@react-spectrum/s2/style' with {type: 'macro'};
import {useState} from 'react';

function Example() {
let [selectedKeys, setSelectedKeys] = useState(new Set()); function performBulkAction() { if (selectedKeys === 'all') { // perform action on all items } else { // perform action on selected items in selectedKeys } }
}

Single selection

In components which support multiple selection, you can limit the selection to a single item using the selectionMode prop. This continues to accept selectedKeys and defaultSelectedKeys as a Set, but it will only contain a single id at a time.

ComboBox, SegmentedControl, and Tabs only support single selection and use the selectedKey prop (singular) rather than selectedKeys.

selectedKeys: lion

import {CardView, AssetCard, CardPreview, Image, Content, Text, type Selection} from '@react-spectrum/s2';
import {style} from '@react-spectrum/s2/style' with {type: 'macro'};
import {useState} from 'react';

function Example() {
  let [selectedKeys, setSelectedKeys] = useState<Selection>(new Set(['lion']));

  return (
    <div>
      <CardView
        aria-label="Nature photos"
        selectionMode="single"
        selectedKeys={selectedKeys}
        onSelectionChange={setSelectedKeys}
        styles={style({width: 360, height: 320})}
      >
        <AssetCard id="desert" textValue="Desert Sunset">
          <CardPreview>
            <Image src="https://images.unsplash.com/photo-1705034598432-1694e203cdf3?q=80&w=600&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" width={600} height={400} />
          </CardPreview>
          <Content>
            <Text slot="title">Desert Sunset</Text>
            <Text slot="description">PNG • 2/3/2024</Text>
          </Content>
        </AssetCard>
        <AssetCard id="hiking" textValue="Hiking Trail">
          <CardPreview>
            <Image src="https://images.unsplash.com/photo-1722233987129-61dc344db8b6?q=80&w=600&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" width={600} height={900} />
          </CardPreview>
          <Content>
            <Text slot="title">Hiking Trail</Text>
            <Text slot="description">JPEG • 1/10/2022</Text>
          </Content>
        </AssetCard>
        <AssetCard id="lion" textValue="Lion">
          <CardPreview>
            <Image src="https://images.unsplash.com/photo-1629812456605-4a044aa38fbc?q=80&w=600&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" width={600} height={899} />
          </CardPreview>
          <Content>
            <Text slot="title">Lion</Text>
            <Text slot="description">JPEG • 8/28/2021</Text>
          </Content>
        </AssetCard>
        <AssetCard id="mountain" textValue="Mountain Sunrise">
          <CardPreview>
            <Image src="https://images.unsplash.com/photo-1722172118908-1a97c312ce8c?q=80&w=600&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" width={600} height={900} />
          </CardPreview>
          <Content>
            <Text slot="title">Mountain Sunrise</Text>
            <Text slot="description">PNG • 3/15/2015</Text>
          </Content>
        </AssetCard>
      </CardView>
      <p className={style({font: 'body'})}>selectedKeys: {selectedKeys === 'all' ? 'all' : [...selectedKeys].join(', ')}</p>
    </div>
  );
}

Item actions

In addition to selection, some collection components support item actions via the onAction prop. When nothing is selected, clicking, tapping, or pressing the Enter key triggers the item action. Items may be selected using the checkbox, or by pressing the Space key. When at least one item is selected, clicking or tapping a row toggles the selection.

On touch devices, actions are the primary tap interaction. Long pressing enters selection mode, which temporarily swaps the selection behavior to where tapping toggles the selection. Deselecting all items exits selection mode and reverts the selection behavior back to where tapping triggers the action. Keyboard behaviors are unaffected.

import {CardView, AssetCard, CardPreview, Image, Content, Text} from '@react-spectrum/s2';
import {style} from '@react-spectrum/s2/style' with {type: 'macro'};

<CardView
  aria-label="Nature photos"
  selectionMode="multiple"
  styles={style({width: 'full', height: 320})}
>
  <AssetCard
    id="desert"
    textValue="Desert Sunset"
    onAction={() => alert('Opening Desert Sunset')}>
    <CardPreview>
      <Image src="https://images.unsplash.com/photo-1705034598432-1694e203cdf3?q=80&w=600&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" width={600} height={400} />
    </CardPreview>
    <Content>
      <Text slot="title">Desert Sunset</Text>
      <Text slot="description">PNG • 2/3/2024</Text>
    </Content>
  </AssetCard>
  <AssetCard
    id="hiking"
    textValue="Hiking Trail"
    onAction={() => alert('Opening Hiking Trail')}>
    <CardPreview>
      <Image src="https://images.unsplash.com/photo-1722233987129-61dc344db8b6?q=80&w=600&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" width={600} height={900} />
    </CardPreview>
    <Content>
      <Text slot="title">Hiking Trail</Text>
      <Text slot="description">JPEG • 1/10/2022</Text>
    </Content>
  </AssetCard>
  <AssetCard
    id="lion"
    textValue="Lion"
    onAction={() => alert('Opening Lion')}>
    <CardPreview>
      <Image src="https://images.unsplash.com/photo-1629812456605-4a044aa38fbc?q=80&w=600&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" width={600} height={899} />
    </CardPreview>
    <Content>
      <Text slot="title">Lion</Text>
      <Text slot="description">JPEG • 8/28/2021</Text>
    </Content>
  </AssetCard>
</CardView>

In dynamic collections, it may be more convenient to use the onAction prop at the collection level instead of on individual items. This receives the id of the pressed item.

import {CardView, AssetCard, CardPreview, Image, Content, Text} from '@react-spectrum/s2';
import {style} from '@react-spectrum/s2/style' with {type: 'macro'};

<CardView aria-label="Nature photos" selectionMode="multiple" onAction={id => alert(`Opening ${id}`)} styles={style({width: 'full', height: 320})} items={photos}> {photo => ( <AssetCard id={photo.id} textValue={photo.title}> <CardPreview> <Image src={photo.image} width={photo.width} height={photo.height} /> </CardPreview> <Content> <Text slot="title">{photo.title}</Text> <Text slot="description">{photo.description}</Text> </Content> </AssetCard> )} </CardView>

Disabled items

An item can be disabled with the isDisabled prop. By default, disabled items are not focusable, selectable, or actionable. When disabledBehavior="selection", only selection is disabled.

import {CardView, AssetCard, CardPreview, Image, Content, Text} from '@react-spectrum/s2';
import {style} from '@react-spectrum/s2/style' with {type: 'macro'};

<CardView
  aria-label="Pokemon"
  selectionMode="multiple"
  styles={style({width: 'full', height: 320})}
>
  <AssetCard id="charizard" textValue="Charizard">
    <CardPreview>
      <Image src="https://img.pokemondb.net/sprites/home/normal/2x/avif/charizard.avif" />
    </CardPreview>
    <Content>
      <Text slot="title">Charizard</Text>
      <Text slot="description">Fire, Flying • Level 67</Text>
    </Content>
  </AssetCard>
  <AssetCard id="blastoise" textValue="Blastoise">
    <CardPreview>
      <Image src="https://img.pokemondb.net/sprites/home/normal/2x/avif/blastoise.avif" />
    </CardPreview>
    <Content>
      <Text slot="title">Blastoise</Text>
      <Text slot="description">Water • Level 56</Text>
    </Content>
  </AssetCard>
  <AssetCard id="venusaur" textValue="Venusaur" isDisabled>
    <CardPreview>
      <Image src="https://img.pokemondb.net/sprites/home/normal/2x/avif/venusaur.avif" />
    </CardPreview>
    <Content>
      <Text slot="title">Venusaur</Text>
      <Text slot="description">Grass, Poison • Level 83</Text>
    </Content>
  </AssetCard>
  <AssetCard id="pikachu" textValue="Pikachu">
    <CardPreview>
      <Image src="https://img.pokemondb.net/sprites/home/normal/2x/avif/pikachu.avif" />
    </CardPreview>
    <Content>
      <Text slot="title">Pikachu</Text>
      <Text slot="description">Electric • Level 100</Text>
    </Content>
  </AssetCard>
</CardView>

In dynamic collections, it may be more convenient to use the disabledKeys prop at the collection level instead of isDisabled on individual items. This accepts a list of item ids that are disabled.

import {CardView, AssetCard, CardPreview, Image, Content, Text} from '@react-spectrum/s2';
import {style} from '@react-spectrum/s2/style' with {type: 'macro'};

<CardView aria-label="Pokemon" selectionMode="multiple" disabledKeys={[3]} styles={style({width: 'full', height: 320})} items={items}> {item => ( <AssetCard id={item.id} textValue={item.name}> <CardPreview> <Image src={`https://img.pokemondb.net/sprites/home/normal/2x/avif/${item.name.toLowerCase()}.avif`} /> </CardPreview> <Content> <Text slot="title">{item.name}</Text> <Text slot="description">{item.type} • Level {item.level}</Text> </Content> </AssetCard> )} </CardView>