KKiotaPay UI
Browse documentation

Multi Select

Every option is a pill and one tap flips it. For short lists picked several at a time, this beats a dropdown — there is no popover to reopen between choices.

Installation

pnpm dlx shadcn@latest add https://kiotapay-ui-staging.staging.kiotapay.com/r/multi-select.json

Usage

'use client'

import * as React from 'react'

import { MultiSelect } from '@/components/custom/multi-select'

const DAYS = [
  { id: 'mon', label: 'Monday' },
  { id: 'tue', label: 'Tuesday' },
]

export function WorkingDaysField() {
  const [days, setDays] = React.useState<(string | number)[]>(['mon'])

  return <MultiSelect items={DAYS} label="Working days" value={days} onChange={setDays} />
}

Props

PropTypeDefaultDescription
items{ id: string | number; label: string }[]—Required. Options.
labelstring—Optional heading above the pills.
value(string | number)[]—Controlled selection.
defaultValue(string | number)[][]Initial selection when uncontrolled.
onChange(selected: (string | number)[]) => void—Receives the whole next array.
disabledbooleanfalseBlocks every pill.

Behaviour notes

Controlled or uncontrolled, decided per render. Passing value makes the prop the source of truth; omitting it lets the component hold the selection. The mode is read from the prop on each render rather than mirrored into state by an effect — mirroring renders the stale copy first and corrects it after, which shows up as a visible flicker whenever the parent rejects a selection.

Pills report their state. Each carries aria-pressed, so the selection is available to assistive technology and not only to the eye.

Use Checkbox Group instead when the list is long enough to want filtering, or when options need composite labels from an API shape.