Skip to content
  1. Keyline UI
  2. Multiselect
Keyline UIComponents
  • Get started
    • Installation
    • Theming
    • Modes
    • Configuration
    • Composition
  • Actions
    • Button
    • Button group
    • Split button
    • Toggle
    • Toggle group
    • Toolbar
    • Menu
    • Menubar
    • Context menu
  • Inputs
    • Field and form
    • Checkbox
    • Checkbox group
    • Radio group
    • Switch
    • Slider
    • Segmented
    • Select
    • Native select
    • Combobox
    • Multiselect
    • Number field
    • OTP field
    • Time field
    • Date picker and calendar
    • Date range
  • Data
    • Table
    • Chart
    • Stat and meter
    • Status and badge
    • Progress
    • Skeleton
    • Empty
    • Avatar
    • Kbd
    • Marker
  • Navigation
    • Tabs
    • Breadcrumb
    • Pagination
    • Nav
    • Navigation menu
    • Command menu
  • Overlays
    • Dialog
    • Alert dialog
    • Drawer
    • Sheet
    • Popover
    • Hover card
    • Tooltip
    • Toast
    • Notice
  • Layout
    • App shell
    • Sidebar
    • Panel
    • Resizable
    • Scroll area
    • Accordion
    • Collapsible
    • Carousel
    • Inspector
    • Item
    • Direction
  • Conversation
    • Message
    • Bubble
    • Message scroller
    • Attachment
    • Questionnaire
  • Blocks
    • Shipments

Multiselect

Search and pick several values. Each choice becomes a yellow token in the field with its own remove cell, and chosen options stay listed while new matches arrive.

Loading the live example…

"use client"

import { useState } from "react"
import { Multiselect, type MultiselectOption } from "@/components/keyline/ui/multiselect"

const PEOPLE: MultiselectOption[] = [
  { value: "pam", label: "Pam Beesly", description: "Reception" },
  { value: "angela", label: "Angela Martin", description: "Accounting" },
  { value: "oscar", label: "Oscar Martinez", description: "Accounting" },
  { value: "kevin", label: "Kevin Malone", description: "Accounting" },
  { value: "darryl", label: "Darryl Philbin", description: "Warehouse" },
  { value: "toby", label: "Toby Flenderson", description: "HR" },
  { value: "creed", label: "Creed Bratton", description: "Quality assurance" },
]

// a local list behind Multiselect's async source
function findPeople(query: string, signal: AbortSignal): Promise<MultiselectOption[]> {
  return new Promise((resolve) => {
    const t = setTimeout(() => resolve(PEOPLE.filter((p) => `${p.label} ${p.description}`.toLowerCase().includes(query.toLowerCase()))), 250)
    signal.addEventListener("abort", () => clearTimeout(t))
  })
}

export function MemoRecipients() {
  const [to, setTo] = useState<MultiselectOption[]>([PEOPLE[1]])

  return (
    <div className="grid max-w-[480px] gap-3">
      <Multiselect label="Send the expense memo to" placeholder="Add people" noun={["person", "people"]} loadOptions={findPeople} value={to} onValueChange={setTo} />
      <p role="status" className="text-[14px] text-kl-ink-2">
        {to.length ? `${to.length} ${to.length === 1 ? "person" : "people"} will get it.${to.some((p) => p.value === "toby") ? " Michael will ask why Toby is on it." : ""}` : "Nobody yet. Accounting usually wants it."}
      </p>
    </div>
  )
}

Installation

npx shadcn@latest add @keyline-ui/multiselect

Run it where your components.json is. The first time, map the @keyline-ui registry: one command, in Installation.

Usage

import { Multiselect } from "@/components/keyline/ui/multiselect"
<Multiselect
  label="Send the memo to"
  noun={["person", "people"]}
  loadOptions={findPeople}
  value={to}
  onValueChange={setTo}
/>

Props

PropTypeDefaultDescription
labelReactNodeDefault —Names the field. Required.
loadOptions(query, signal) => Promise<MultiselectOption[]>Default —Required. Called on open with an empty query, then on each edit; a newer search aborts the older one. For a fixed list, filter it here.
valueMultiselectOption[]Default —The chosen options, whole: { value, label, description?, count?, disabled? }. With onValueChange, or start with defaultValue.
noun[string, string]Default ["item", "items"]Names what is listed, in the status line.
layout"control" | "cell"Default "control"cell drops the frame and hides the label, for a toolbar.

Backspace in an empty input removes the last token. For a short fixed list, Checkbox group shows every choice at once. Built on Base UI Combobox.

Sheet 26 of 73PreviousComboboxNextNumber field
On this page