Primitives
Multi select
Chips in the field, search, groups, select all; ↑ ↓ Enter, Backspace removes the last; max says why it stops.
Gulshan ParkingBanani Tower+2
Grouped by city; type to search; Backspace removes the last.
Choose gates…
max stops the fourth choice and says why.
multi-select.tsx
"use client";
import { FormField, MultiSelect, type MultiSelectOption } from "@waveinno/ui";
import { Building2, Hospital, ShoppingBag } from "lucide-react";
import * as React from "react";
const SITES: MultiSelectOption[] = [
{ value: "gulshan", label: "Gulshan Parking", description: "3 levels · 420 spaces", icon: <Building2 />, group: "Dhaka" },
{ value: "banani", label: "Banani Tower", description: "2 levels · 180 spaces", icon: <Building2 />, group: "Dhaka" },
{ value: "bashundhara", label: "Bashundhara City", description: "4 levels · 1,400 spaces", icon: <ShoppingBag />, group: "Dhaka" },
{ value: "square", label: "Square Hospital", description: "Staff and visitors", icon: <Hospital />, group: "Dhaka" },
{ value: "gec", label: "GEC Circle", description: "Open lot", icon: <Building2 />, group: "Chattogram" },
{ value: "agrabad", label: "Agrabad Tower", description: "1 level · 90 spaces", icon: <Building2 />, group: "Chattogram" },
{ value: "zindabazar", label: "Zindabazar", description: "Open lot", icon: <Building2 />, group: "Sylhet", disabled: true },
];
export default function MultiSelectDemo() {
const [sites, setSites] = React.useState(["gulshan", "banani", "bashundhara", "square"]);
return (
<div className="grid gap-6 md:grid-cols-2">
<FormField label="Sites this manager can see" hint="Grouped by city; type to search; Backspace removes the last.">
<MultiSelect options={SITES} value={sites} onValueChange={setSites} maxShown={2} aria-label="Sites" />
</FormField>
<FormField label="Up to three gates" hint="max stops the fourth choice and says why.">
<MultiSelect
max={3}
placeholder="Choose gates…"
aria-label="Gates"
options={["Gate 1 · entry", "Gate 2 · exit", "Gate 3 · entry", "Gate 4 · exit", "Gate 5 · staff"].map((g) => ({ value: g, label: g }))}
/>
</FormField>
</div>
);
}Import#
Everything comes from the package root. The example above uses:
your-component.tsx
import { FormField, MultiSelect, type MultiSelectOption } from "@waveinno/ui";Props#
Read from the source of this version, so they match the code exactly.
<MultiSelect>
src/components/ui/multi-select.tsx| Prop | Type | Default | Description |
|---|---|---|---|
optionsrequired | MultiSelectOption[] | — | |
value | string[] | — | |
defaultValue | string[] | [] | |
onValueChange | (value: string[]) => void | — | |
placeholder | string | "Choose…" | |
maxShown | number | 3 | Chips shown in the field before "+N". Default 3. |
max | number | — | |
bulk | boolean | true | Offer "Select all" and "Clear" under the list. Default true. |
disabled | boolean | — | |
state | InputState | "default" | |
labels | Partial<Record<"search" | "empty" | "selectAll" | "clear" | "selected" | "limit" | "remove", string>> | — | |
aria-label | string | — | |
className | string | — |
<FormField>
src/components/ui/form-field.tsx| Prop | Type | Default | Description |
|---|---|---|---|
label | React.ReactNode | — | |
htmlFor | string | — | The control's id, so clicking the label focuses it. |
required | boolean | — | |
hint | React.ReactNode | — | |
error | React.ReactNode | — | |
warning | React.ReactNode | — | |
success | React.ReactNode | — | |
count | { value: number; max: number } | — | Character counter, e.g. { value: note.length, max: 240 }. |
className | string | — | |
childrenrequired | React.ReactNode | — |
Install#
If the project does not have the package yet, add it, then follow the setup for Next.js or Vite once.
$ npm install @waveinno/ui