Skip to content
Dethink Components

ComponentsMultiSelect

MultiSelect

Let users search for and choose several options.

On this page

Use this component from the local workspace package. Follow the setup guide first. A public npm package and registry are not available yet.

View registry files

Import the component into your page or component file.

Usage
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.

0 options selectedSearch and select every team that should receive updates.
Show sourceexamples/multi-select/basic.tsx
examples/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.

Finance
1 option selected

Selected audiences: finance

Show sourceexamples/multi-select/controlled.tsx
examples/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.

0 options selectedChoose at least one active owner.
OperationsRevOps
2 options selected
Finance
1 option selected
Show sourceexamples/multi-select/states.tsx
examples/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.

OperationsFinanceRevOpsCustomer success
4 options selected
Show sourceexamples/multi-select/theme-and-wrapping.tsx
examples/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.

Finance
1 option selectedEach selected value serializes as a repeated teams field.
OpenReview
2 options selected
Show sourceexamples/multi-select/recipe-filter-bar.tsx
examples/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.

MultiSelect props
PropWhat it doesDefault
value / defaultValue / onValueChangestring[] / string[] / (value) => voidControlled or uncontrolled selected option values.[]
inputValue / defaultInputValue / onInputValueChangestring / string / (text) => voidControlled or uncontrolled text for the searchable input.""
childrenMultiSelectItem nodes | (item) => MultiSelectItemStatic 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 / errorMessageReactNodeThe field label, help text, and error message.Not set
placeholder / searchPlaceholder / emptyMessageReactNodeCopy 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 / invalidbooleanMakes the field required, disabled, read-only, or invalid.false
namestringRepeated hidden input name for native form submission.Not set
MultiSelectItem props
PropWhat it doesDefault
valuestringUnique option value used for selection and form submission.Not set
childrenReactNodeVisible option label.Not set
textValuestringPlain text used when option children are rich nodes.Not set
disabledbooleanDisables this option.false