Components · Multi-select
Components

Multi-select

A combobox that keeps its list open while you pick several things. We have exactly one place for it — filtering by rating category — and that constraint is what keeps it from becoming a tag input, a token field and a search box at the same time.

Live

Filter by category

Showing all 412 reviews.

Behaviour

SituationBehaviourWhy
An option is chosenThe list stays open and the option stays in place with a checkClosing after each pick makes selecting three things a nine-action job
An option is chosenIt is not removed from the listRemoving it makes the list jump under the pointer
The field has textOptions filter; the query clears on selectThe next pick starts from the whole list
Backspace on an empty fieldRemoves the last chipThe one keyboard shortcut people already expect here
More than 1 selectedThe chips collapse to a single count — 4 categories selectedThe control must stay one line tall; two long names plus the input already overflow it
A collapsed selectionIndividual removal moves to the list, where every option is a toggleA count chip has nothing to remove; the list already shows what is selected
Nothing selectedPlaceholder reads All categoriesEmpty means unfiltered, and must say so
EscapeCloses the list, keeps the selectionEscape dismisses the popup, it does not undo work
BlurCloses; selection is already appliedThere is no Apply button and no pending state

Selection applies immediately. A multi-select with an Apply button is two mental models at once — people tick, look at the results, see nothing change, and tick again.

Specification

PropertyValueWhy
Controlrole="combobox" + aria-expanded + aria-controlsThe APG combobox pattern, unmodified
Listrole="listbox" aria-multiselectable="true"Announces that more than one is allowed, before the first pick
Optionsrole="option" + aria-selectedSelected state read on arrival, not inferred from a tick
FocusStays in the input; aria-activedescendant movesTyping must keep working while arrowing
Row height44pxThe floor, met by the row rather than by the tick
ChipOne named chip with a 24px remove button; a count chip beyond oneThe named chip is a target and its remove button is a second one
Selected cueA check, plus --color-primary-subtleColour is never the only signal
Announcementaria-live="polite" count after each change“3 of 7 categories selected”

When not to use it

  • Under 8 options, all visible at once — use a checkbox group. It shows every option without a click and needs no ARIA at all. Our seven categories sit right on this line, and the multi-select earns its place only because the filter bar cannot afford seven rows.
  • Two or three options that need explaining — use a choicebox.
  • Free text as well as options — that is a tag input, and we do not have one. Do not grow this component into it.
  • One choice only — that is Select, which closes on pick and behaves differently on purpose.

Rules

  • Order never changes. Selected options do not float to the top, ever — the list is a stable map, and rearranging it under the pointer causes mis-clicks.
  • Empty means everything. No selection is not an empty result set; it is an unfiltered one, and the placeholder must say so.
  • Clear-all is one control, labelled, and it appears only when something is selected. Past one selection it is the only control in the field that removes anything — everything else happens in the list.
  • Never lose a selection on re-render. Refiltering the underlying data must not clear chips.
  • The count is announced, not just drawn. Chips are visual; the live region is what a screen-reader user has.

React

From @sportingscouter/ui-react. The component adds no visual values of its own — every class comes from the same tv() recipe the design system defines, so the two cannot drift.

import { MultiSelect } from '@sportingscouter/ui-react';

<MultiSelect
  label="Categories to compare"
  placeholder="Choose any"
  options={CATEGORIES}
  value={picked}
  onValueChange={setPicked}
  maxChips={3}
/>

{/* The list STAYS OPEN on toggle — closing after one pick
    forces a reopen per value, which is the thing a
    multi-select exists to avoid. Backspace on the closed
    trigger removes the last chip. */}
Sporting Scouter Design System
Every token on this site is generated from theme.css.