# MultiSelect

> Choose several values; selections appear as removable tags and the list stays open while you pick.

Category: Forms · Status: stable · Package: @bindshq/stance-react@0.1.1 · Docs: https://stanceui.com/components/multi-select

Use MultiSelect to choose any number of values from a list, such as the departments a signature template applies to. Selected values show as removable tags in the field. Turn on searchable for long lists. For fewer than six options consider a CheckboxGroup, which shows every choice at once.

## Import

```tsx
import { MultiSelect, type SelectOptions } from '@bindshq/stance-react';
```

Styles are imported once per app: `@bindshq/stance-tokens/css` and `@bindshq/stance-react/styles.css`.

## Examples

### Basic

```tsx
import { FormField, MultiSelect } from '@bindshq/stance-react';
import type { SelectOptions } from '@bindshq/stance-react';

const departments: SelectOptions = [
  { value: 'sales', label: 'Sales' },
  { value: 'marketing', label: 'Marketing' },
  { value: 'legal', label: 'Legal' },
  { value: 'finance', label: 'Finance' },
  { value: 'support', label: 'Customer support' },
  { value: 'engineering', label: 'Engineering' },
  { value: 'people', label: 'People & culture', disabled: true },
];

export function Basic() {
  return (
    <div style={{ width: 360 }}>
      <FormField
        label="Apply template to"
        description="Members of these departments receive the signature."
      >
        <MultiSelect
          options={departments}
          defaultValue={['sales', 'marketing']}
          placeholder="Choose departments"
        />
      </FormField>
    </div>
  );
}
```

### Searchable

Type to filter; Backspace in an empty search removes the last tag.

```tsx
import { FormField, MultiSelect } from '@bindshq/stance-react';
import type { SelectOptions } from '@bindshq/stance-react';

const departments: SelectOptions = [
  { value: 'sales', label: 'Sales' },
  { value: 'marketing', label: 'Marketing' },
  { value: 'legal', label: 'Legal' },
  { value: 'finance', label: 'Finance' },
  { value: 'support', label: 'Customer support' },
  { value: 'engineering', label: 'Engineering' },
  { value: 'people', label: 'People & culture', disabled: true },
];

export function Searchable() {
  return (
    <div style={{ width: 360 }}>
      <FormField label="Departments">
        <MultiSelect options={departments} searchable placeholder="Search departments" />
      </FormField>
    </div>
  );
}
```

### Controlled with a tag limit

```tsx
import * as React from 'react';
import { FormField, MultiSelect } from '@bindshq/stance-react';
import type { SelectOptions } from '@bindshq/stance-react';

const departments: SelectOptions = [
  { value: 'sales', label: 'Sales' },
  { value: 'marketing', label: 'Marketing' },
  { value: 'legal', label: 'Legal' },
  { value: 'finance', label: 'Finance' },
  { value: 'support', label: 'Customer support' },
  { value: 'engineering', label: 'Engineering' },
  { value: 'people', label: 'People & culture', disabled: true },
];

export function Controlled() {
  const [value, setValue] = React.useState<string[]>([
    'sales',
    'legal',
    'finance',
    'support',
    'engineering',
  ]);
  return (
    <div style={{ display: 'grid', gap: 8, width: 360 }}>
      <FormField label="Departments">
        <MultiSelect options={departments} value={value} onValueChange={setValue} maxTags={2} />
      </FormField>
      <span style={{ color: 'var(--st-color-fg-muted)', fontSize: 'var(--st-font-size-sm)' }}>
        {value.length} departments selected
      </span>
    </div>
  );
}
```

### Validation

```tsx
import { FormField, MultiSelect } from '@bindshq/stance-react';
import type { SelectOptions } from '@bindshq/stance-react';

const departments: SelectOptions = [
  { value: 'sales', label: 'Sales' },
  { value: 'marketing', label: 'Marketing' },
  { value: 'legal', label: 'Legal' },
  { value: 'finance', label: 'Finance' },
  { value: 'support', label: 'Customer support' },
  { value: 'engineering', label: 'Engineering' },
  { value: 'people', label: 'People & culture', disabled: true },
];

export function Invalid() {
  return (
    <div style={{ width: 360 }}>
      <FormField label="Departments" error="Select at least one department" required>
        <MultiSelect options={departments} />
      </FormField>
    </div>
  );
}
```

## API reference

| Component | Prop | Type | Default | Description |
| --- | --- | --- | --- | --- |
| MultiSelect | `options` (required) | `SelectOptions` | None | Options or groups, as in Select. |
| MultiSelect | `value / defaultValue` | `string[]` | `[]` | Selected values (controlled or initial). |
| MultiSelect | `onValueChange` | `(value: string[]) => void` | None | Called on every change. |
| MultiSelect | `placeholder` | `string` | `'Select…'` | Shown when nothing is selected. |
| MultiSelect | `searchable` | `boolean` | `false` | Replace the button with a filter input. |
| MultiSelect | `searchPlaceholder` | `string` | `'Search…'` | Placeholder of the search input once values are selected. |
| MultiSelect | `showActions` | `boolean` | `true` | Show “Select all” and a live count above the list. |
| MultiSelect | `maxTags` | `number` | `3` | Collapse further tags into “+N”. |
| MultiSelect | `name / form` | `string` | None | Submits one hidden input per selected value. |
| MultiSelect | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Minimum control height. |
| MultiSelect | `disabled / required / invalid` | `boolean` | None | Also inherited from FormField. |
| MultiSelect | `open / defaultOpen / onOpenChange` | `boolean / function` | None | Popup open state. |
| MultiSelect | `removeLabel` | `(label: string) => string` | ``Remove ${label}`` | Accessible label for tag remove buttons (localisation). |
| MultiSelect | `emptyText` | `ReactNode` | `'No matches'` | Shown when the search matches nothing. |

## Accessibility

- The control is a combobox controlling a listbox with aria-multiselectable="true"; options expose aria-selected.
- Selected tags are a labelled list; each has a remove button with a descriptive label.
- The “N selected” count is announced politely as selections change.
- Choosing an option keeps the list open so several values can be picked in one pass.

## Keyboard

| Keys | Action |
| --- | --- |
| `ArrowDown` `ArrowUp` `Enter` | Open the list. |
| `ArrowDown` `ArrowUp` | Move the active option. |
| `Enter` `Space` | Toggle the active option (Space only without search). |
| `Ctrl+A` `⌘A` | Select or deselect all visible options. |
| `Backspace` | With an empty search, remove the last selected value. |
| `Escape` `Tab` | Close the list. |

## Guidelines

**Do**

- Use searchable when there are more than about 15 options.
- Show a count or summary when many values are selected.

**Don’t**

- Use MultiSelect when only one value is allowed.
- Hide important constraints, such as a maximum, until after submission.

## Related

- https://stanceui.com/components/select
- https://stanceui.com/components/combobox
- https://stanceui.com/components/checkbox-group
- https://stanceui.com/components/tag
