MDK Logo

MultiSelect

Multi-select picker built on Radix Popover + Checkbox. Sibling to `Select` for cases where consumers need to pick more than one option (filter rows, multi-ta…

Multi-select picker built on Radix Popover + Checkbox. Sibling to <Select> for cases where consumers need to pick more than one option (filter rows, multi-target actions, tag-style inputs). The popover stays open on toggle; Esc / outside-click closes it. Selected values render as removable chips in the trigger, with an optional "clear all" affordance and a +N more overflow chip via maxSelectedDisplay.

Controlled vs uncontrolled is decided by the presence of value (mirrors Radix Select's convention): pass value for controlled, defaultValue for uncontrolled.

import { MultiSelect } from "@tetherto/mdk-react-devkit";
PropStatusTypeDefaultDescription
optionsRequiredMultiSelectOption[]
valueOptionalstring[] | undefinedControlled selected values. Omit to use `defaultValue` for uncontrolled mode.
defaultValueOptionalstring[] | undefinedInitial values for uncontrolled mode. Ignored when `value` is provided.
onValueChangeOptional((next: string[]) => void) | undefined
placeholderOptionalReact.ReactNode
disabledOptionalboolean | undefined
sizeOptionalMultiSelectSize | undefined
variantOptionalMultiSelectVariant | undefined
emptyMessageOptionalReact.ReactNodeRendered inside the popover when `options` is empty.
maxSelectedDisplayOptionalnumber | undefinedMax number of selected chips rendered in the trigger before collapsing the rest into a "+N more" badge. `undefined` (default) renders every chip.
classNameOptionalstring | undefined
contentClassNameOptionalstring | undefined
aria-labelOptionalstring | undefinedAccessible label - applied to the trigger button.
idOptionalstring | undefined
nameOptionalstring | undefined

Usage

Pick multiple values from a dropdown. Built on Radix Popover + the core Checkbox so it stays open on toggle - the canonical Radix pattern for multi-select (Radix Select itself is single-select by design).

Use for filter rows (miner type, mining unit, status), multi-target actions, or any tag-style input. For a single-value picker use <Select> instead.

Behaviour notes

  • The popover stays open after toggling an option so multi-pick is ergonomic without re-opening on every change.
  • Clear-all surfaces in the trigger only when >= 2 values are selected.
  • Per-chip x buttons are not in the tab order (the parent trigger handles focus); they remove only that value on click.
  • Keyboard: Enter / Space on the trigger opens the popover, Arrow Down / Up move focus through rows, Space toggles the focused row without closing, Esc closes and returns focus to the trigger.

Example

/** * Runnable example for MultiSelect. */import { MultiSelect, type MultiSelectOption } from '@tetherto/mdk-react-devkit/primitives'import { useState } from 'react'const MINER_TYPES: MultiSelectOption[] = [  { value: 'miner-am-s19xp', label: 'Antminer S19XP' },  { value: 'miner-wm-m56s', label: 'WhatsMiner M56S' },  { value: 'miner-av-a1346', label: 'Avalon A1346' },  { value: 'miner-demo-m1', label: 'Demo M1' },]export const MultiSelectExample = () => {  const [selected, setSelected] = useState<string[]>([])  return (    <MultiSelect      options={MINER_TYPES}      value={selected}      onValueChange={setSelected}      placeholder="Filter by miner type"      maxSelectedDisplay={2}    />  )}