ComponentsMultiSelect
MultiSelect
Let users search for and choose several options.
On this page
Install component source from the public registry, or use the local workspace package. Follow the setup guide first. The npm package is not published yet.
View registry files- React Aria
(react-aria)Accessibility hooks and overlay utilities used by the implementation.
- React Aria Components
(react-aria-components)Accessible interaction primitives, including keyboard behavior, focus handling, and ARIA semantics.
Shared foundation: React, clsx for conditional classes, tailwind-merge for resolving utility-class conflicts. Styles use Tailwind CSS and Dethink CSS variables.
Based on this component’s source and registry composition. Libraries used only in showcase examples are excluded.
Import the component into your page or component file.
import { MultiSelect, MultiSelectItem } from "@dethink/components";Open the list, type to filter, select multiple options, and remove selected chips from the control.
Basic
Static options with label, helper text, and native form name.
Show sourceexamples/multi-select/basic.tsx
"use client";
import { MultiSelect, MultiSelectItem } from "@dethink/components";
export function MultiSelectBasic() {
return (
<div className="mx-auto max-w-sm">
<MultiSelect
description="Search and select every team that should receive updates."
label="Recipient teams"
name="recipientTeams"
placeholder="Choose teams"
>
<MultiSelectItem value="operations">Operations</MultiSelectItem>
<MultiSelectItem value="finance">Finance</MultiSelectItem>
<MultiSelectItem value="revops">RevOps</MultiSelectItem>
<MultiSelectItem value="customer-success">
Customer success
</MultiSelectItem>
</MultiSelect>
</div>
);
}Controlled
Apps can own the selected array and render derived UI next to the field.
Selected audiences: finance
Show sourceexamples/multi-select/controlled.tsx
"use client";
import { useState } from "react";
import {
FieldDescription,
MultiSelect,
MultiSelectItem,
Stack,
} from "@dethink/components";
export function MultiSelectControlled() {
const [value, setValue] = useState(["finance"]);
return (
<div className="mx-auto max-w-sm">
<Stack gap="3">
<MultiSelect
label="Default audiences"
value={value}
onValueChange={setValue}
>
<MultiSelectItem value="operations">Operations</MultiSelectItem>
<MultiSelectItem value="finance">Finance</MultiSelectItem>
<MultiSelectItem value="revops">RevOps</MultiSelectItem>
</MultiSelect>
<FieldDescription>
Selected audiences: {value.join(", ") || "none"}
</FieldDescription>
</Stack>
</div>
);
}Form states
Invalid, required, disabled option, read-only, and disabled control states.
Show sourceexamples/multi-select/states.tsx
"use client";
import { MultiSelect, MultiSelectItem, Stack } from "@dethink/components";
export function MultiSelectStates() {
return (
<div className="mx-auto max-w-sm">
<Stack gap="4">
<MultiSelect
disabledKeys={["finance"]}
errorMessage="Choose at least one active owner."
invalid
label="Active owners"
required
>
<MultiSelectItem value="operations">Operations</MultiSelectItem>
<MultiSelectItem value="finance">Finance</MultiSelectItem>
<MultiSelectItem value="revops">RevOps</MultiSelectItem>
</MultiSelect>
<MultiSelect
readOnly
defaultValue={["operations", "revops"]}
label="Inherited visibility"
>
<MultiSelectItem value="operations">Operations</MultiSelectItem>
<MultiSelectItem value="revops">RevOps</MultiSelectItem>
</MultiSelect>
<MultiSelect disabled defaultValue={["finance"]} label="Locked teams">
<MultiSelectItem value="finance">Finance</MultiSelectItem>
</MultiSelect>
</Stack>
</div>
);
}Theme, RTL, and wrapping
Nested provider tokens, compact density, RTL direction, and narrow chip wrapping.
Show sourceexamples/multi-select/theme-and-wrapping.tsx
"use client";
import {
DethinkProvider,
MultiSelect,
MultiSelectItem,
} from "@dethink/components";
export function MultiSelectThemeAndWrapping() {
return (
<DethinkProvider theme="dark" density="compact" dir="rtl">
<div className="border-border bg-background mx-auto max-w-72 rounded-lg border p-4">
<MultiSelect
defaultValue={["operations", "finance", "revops", "customer-success"]}
label="فرق التقارير"
name="reportTeams"
>
<MultiSelectItem value="operations">Operations</MultiSelectItem>
<MultiSelectItem value="finance">Finance</MultiSelectItem>
<MultiSelectItem value="revops">RevOps</MultiSelectItem>
<MultiSelectItem value="customer-success">
Customer success
</MultiSelectItem>
</MultiSelect>
</div>
</DethinkProvider>
);
}Examples that combine components for common tasks.
Filter bar
Two MultiSelect controls submit repeated query params for teams and statuses in a dense operations filter.
Show sourceexamples/multi-select/recipe-filter-bar.tsx
"use client";
import { Form, MultiSelect, MultiSelectItem, Stack } from "@dethink/components";
const teamItems = [
{ label: "Operations", value: "operations" },
{ label: "Finance", value: "finance" },
{ label: "RevOps", value: "revops" },
{ label: "Customer success", value: "customer-success" },
];
export function MultiSelectRecipeFilterBar() {
return (
<Form action="/invoices" method="get" className="mx-auto max-w-xl">
<div className="border-border bg-muted/20 rounded-lg border p-4">
<Stack gap="4">
<MultiSelect
defaultValue={["finance"]}
description="Each selected value serializes as a repeated teams field."
items={teamItems}
label="Teams"
name="teams"
>
{(item) => (
<MultiSelectItem key={item.value} value={item.value}>
{item.label}
</MultiSelectItem>
)}
</MultiSelect>
<MultiSelect
defaultValue={["open", "review"]}
label="Status"
name="status"
>
<MultiSelectItem value="open">Open</MultiSelectItem>
<MultiSelectItem value="review">Review</MultiSelectItem>
<MultiSelectItem value="paid">Paid</MultiSelectItem>
<MultiSelectItem value="overdue">Overdue</MultiSelectItem>
</MultiSelect>
</Stack>
</div>
</Form>
);
}MultiSelect owns its field anatomy and selected chip rendering.
| Prop | What it does | Default |
|---|---|---|
shouldFilterboolean | Set false when items are already filtered by the server; preserves the provided result order. | true |
value / defaultValue / onValueChangestring[] / string[] / (value) => void | Controlled or uncontrolled selected option values. | [] |
inputValue / defaultInputValue / onInputValueChangestring / string / (text) => void | Controlled or uncontrolled text for the searchable input. | "" |
childrenMultiSelectItem nodes | (item) => MultiSelectItem | Static options, or a render function when passing item data. | Not set |
items / selectedItemsIterable<MultiSelectItemData> | Data-driven options and optional selected item data for stable chip labels. | Not set |
disabledKeysIterable<string> | Option values that cannot be selected. | Not set |
label / description / errorMessageReactNode | The field label, help text, and error message. | Not set |
placeholder / searchPlaceholder / emptyMessageReactNode | Copy for the empty control, chip input, and no-results state. | Not set |
controlSize"sm" | "md" | "lg" | Control height and typography scale. | "md" |
required / disabled / readOnly / invalidboolean | Makes the field required, disabled, read-only, or invalid. | false |
namestring | Repeated hidden input name for native form submission. | Not set |
| Prop | What it does | Default |
|---|---|---|
valuestring | Unique option value used for selection and form submission. | Not set |
childrenReactNode | Visible option label. | Not set |
textValuestring | Plain text used when option children are rich nodes. | Not set |
disabledboolean | Disables this option. | false |