# List Selection

URL: https://ark-ui.com/docs/collections/list-selection
Source: https://raw.githubusercontent.com/chakra-ui/ark/refs/heads/main/website/src/content/pages/collections/list-selection.mdx

Used for managing selection state in list collections.

---

The `useListSelection` hook manages selection state in lists and collections. It supports single and multiple selection
modes with operations like select, deselect, toggle, and clear.

```tsx
import { createListCollection, useListSelection } from 'ark-ripple/collection'

const collection = createListCollection({
  items: [
    { label: 'Apple', value: 'apple' },
    { label: 'Banana', value: 'banana' },
    { label: 'Cherry', value: 'cherry' },
  ],
})

const selection = useListSelection({
  collection,
  selectionMode: 'single',
  deselectable: true,
})

console.log(selection.selectedValues) // ['apple', 'banana', 'cherry']
```

## Examples

### Basic

By default, the hook supports single selection mode that can be deselected.

> Set `deselectable` to `false` to prevent deselecting the current selection.

**Example: list-selection/basic**

```ripple
import { Checkbox } from 'ark-ripple/checkbox';
import { createListCollection, useListSelection } from 'ark-ripple/collection';
import { Check } from 'lucide-ripple';
import styles from 'styles/list-selection.module.css';
import checkboxStyles from 'styles/checkbox.module.css';

const collection = createListCollection(
  {
    items: [
      { label: 'React', value: 'react' },
      { label: 'Vue', value: 'vue' },
      { label: 'Angular', value: 'angular' },
      { label: 'Svelte', value: 'svelte' },
    ],
  },
);

export component Basic() {
  const selection = useListSelection({ collection });

  <div class={styles.Root}>
    <output>
      {'Selected: '}
      {@selection.selectedValues.join(', ') || 'None'}
    </output>
    for (const item of collection.items; key item.value) {
      <Checkbox.Root
        class={styles.Item}
        data-selected={@selection.isSelected(item.value) || undefined}
        checked={@selection.isSelected(item.value)}
        onCheckedChange={() => @selection.select(item.value)}
      >
        <Checkbox.Control class={checkboxStyles.Control}>
          <Checkbox.Indicator class={checkboxStyles.Indicator}>
            <Check />
          </Checkbox.Indicator>
        </Checkbox.Control>
        <Checkbox.Label class={checkboxStyles.Label}>{item.label}</Checkbox.Label>
        <Checkbox.HiddenInput />
      </Checkbox.Root>
    }
  </div>
}

```

### Multiple Selection

Set `selectionMode` to `multiple` to allow multiple items to be selected.

**Example: list-selection/multiple**

```ripple
import { Checkbox } from 'ark-ripple/checkbox';
import { createListCollection, useListSelection } from 'ark-ripple/collection';
import { Check } from 'lucide-ripple';
import styles from 'styles/list-selection.module.css';
import checkboxStyles from 'styles/checkbox.module.css';

const collection = createListCollection(
  {
    items: [
      { label: 'React', value: 'react' },
      { label: 'Vue', value: 'vue' },
      { label: 'Angular', value: 'angular' },
      { label: 'Svelte', value: 'svelte' },
      { label: 'Solid', value: 'solid' },
    ],
  },
);

export component Multiple() {
  const selection = useListSelection({ collection, selectionMode: 'multiple' });

  const handleSelectAll = () => {
    if (@selection.isAllSelected()) {
      @selection.clear();
    } else {
      @selection.setSelectedValues(collection.getValues());
    }
  };

  <div class={styles.Root}>
    <div class={styles.Header}>
      <span class={styles.Count}>
        {@selection.selectedValues.length}
        {' of '}
        {collection.items.length}
        {' selected'}
      </span>
      <button type="button" class={styles.SelectAllButton} onClick={handleSelectAll}>
        {@selection.isAllSelected() ? 'Deselect all' : 'Select all'}
      </button>
    </div>
    for (const item of collection.items; key item.value) {
      <Checkbox.Root
        class={styles.Item}
        data-selected={@selection.isSelected(item.value) || undefined}
        checked={@selection.isSelected(item.value)}
        onCheckedChange={() => @selection.select(item.value)}
      >
        <Checkbox.Control class={checkboxStyles.Control}>
          <Checkbox.Indicator class={checkboxStyles.Indicator}>
            <Check />
          </Checkbox.Indicator>
        </Checkbox.Control>
        <Checkbox.Label class={checkboxStyles.Label}>{item.label}</Checkbox.Label>
        <Checkbox.HiddenInput />
      </Checkbox.Root>
    }
  </div>
}

```

### Range Selection

Here's an example of how to implement range selection that extends the selection from the first selected item to the
clicked item.

**Example: list-selection/range**

```ripple
import { Checkbox } from 'ark-ripple/checkbox';
import { createListCollection, useListSelection } from 'ark-ripple/collection';
import { Check } from 'lucide-ripple';
import styles from 'styles/list-selection.module.css';
import checkboxStyles from 'styles/checkbox.module.css';

const collection = createListCollection(
  {
    items: [
      { label: 'React', value: 'react' },
      { label: 'Vue', value: 'vue' },
      { label: 'Angular', value: 'angular' },
      { label: 'Svelte', value: 'svelte' },
      { label: 'Solid', value: 'solid' },
    ],
  },
);

export component Range() {
  const selection = useListSelection({ collection, selectionMode: 'multiple' });

  const handleItemClick = (value: string, event: MouseEvent) => {
    event.preventDefault();
    const firstSelected = @selection.firstSelectedValue;
    if (event.shiftKey && firstSelected) {
      @selection.extend(firstSelected, value);
    } else if (event.ctrlKey || event.metaKey) {
      @selection.toggle(value);
    } else {
      @selection.replace(value);
    }
  };

  <div class={styles.Root}>
    <output>
      {'Selected: '}
      {@selection.selectedValues.join(', ') || 'None'}
    </output>
    for (const item of collection.items; key item.value) {
      <Checkbox.Root
        class={styles.Item}
        data-selected={@selection.isSelected(item.value) || undefined}
        checked={@selection.isSelected(item.value)}
        onClick={(e) => handleItemClick(item.value, e)}
      >
        <Checkbox.Control class={checkboxStyles.Control}>
          <Checkbox.Indicator class={checkboxStyles.Indicator}>
            <Check />
          </Checkbox.Indicator>
        </Checkbox.Control>
        <Checkbox.Label class={checkboxStyles.Label}>{item.label}</Checkbox.Label>
        <Checkbox.HiddenInput />
      </Checkbox.Root>
    }
    <p class={styles.HelperText}>
      {'Click to select • Shift+Click for range • Cmd/Ctrl+Click to toggle'}
    </p>
  </div>
}

```

## API Reference

### Props

- **collection** (`ListCollection<T>`) - The collection to manage selection for
- **selectionMode** (`'single' | 'multiple' | 'none'`, default: `'single'`) - The selection mode
- **deselectable** (`boolean`, default: `true`) - Whether selected items can be deselected
- **initialSelectedValues** (`string[]`, default: `[]`) - Initial selected values
- **resetOnCollectionChange** (`boolean`, default: `false`) - Whether to reset selection when collection changes

### Return Value

The hook returns an object with the following properties and methods:

#### State Properties

- **selectedValues** (`string[]`) - Array of currently selected values
- **isEmpty** (`boolean`) - Whether no items are selected
- **firstSelectedValue** (`string | null`) - The first selected value in collection order
- **lastSelectedValue** (`string | null`) - The last selected value in collection order

#### Query Methods

- **isSelected** (`(value: string | null) => boolean`) - Check if a value is selected
- **canSelect** (`(value: string) => boolean`) - Check if a value can be selected
- **isAllSelected** (`() => boolean`) - Check if all items are selected
- **isSomeSelected** (`() => boolean`) - Check if some items are selected

#### Selection Methods

- **select** (`(value: string, forceToggle?: boolean) => void`) - Select a value
- **deselect** (`(value: string) => void`) - Deselect a value
- **toggle** (`(value: string) => void`) - Toggle selection of a value
- **replace** (`(value: string | null) => void`) - Replace selection with a single value
- **extend** (`(anchorValue: string, targetValue: string) => void`) - Extend selection from anchor to target
- **setSelectedValues** (`(values: string[]) => void`) - Set the selected values
- **setSelection** (`(values: string[]) => void`) - Set the selection (alias for setSelectedValues)
- **clear** (`() => void`) - Clear all selections
- **resetSelection** (`() => void`) - Reset selection to initial state