Multi-select
Select several values while keeping the field readable.
fieldchoice
Preview
Multi-select preview
Live · keyboard ready
Skills
When to use
- Picking several values from a fixed list in a compact field, such as labels or assignees.
- Filters where the chosen values should show in the field with a +N overflow.
When not to use
- Use chip-group when every option should stay visible.
- Use tag-input for free-form values.
- Use select for a single value.
shadcn CLI
Also installs (1)
- manicat-foundation
Manual install(+)
registry.jsonjson
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "multi-select",
"type": "registry:ui",
"title": "Multi-select",
"description": "Select several values while keeping the field readable.",
"categories": [
"inputs"
],
"dependencies": [
"lucide-react",
"motion"
],
"registryDependencies": [
"https://manicat.viraza.net/r/manicat-foundation.json"
],
"files": [
{
"path": "registry/components/multi-select/multi-select.tsx",
"type": "registry:component",
"target": "@components/manicat/multi-select/multi-select.tsx"
},
{
"path": "registry/components/multi-select/multi-select.module.css",
"type": "registry:component",
"target": "@components/manicat/multi-select/multi-select.module.css"
}
],
"docs": "Docs and live preview: https://uiarc.dev/components/multi-select",
"meta": {
"tier": "free",
"kind": "component",
"docs": "https://manicat.viraza.net/components/multi-select",
"markdown": "https://uiarc.dev/components/multi-select/markdown",
"tags": [
"field",
"choice"
]
}
}Usage
Install the component, then copy this into your app. Change the content and the props to fit your screen.
example.tsxtsx
"use client";
import { useState } from "react";
import { MultiSelect } from "@/components/manicat/multi-select/multi-select";
export function MultiSelectExample() {
const [value, setValue] = useState(["design", "motion"]);
return (
<MultiSelect
label="Skills"
value={value}
onValueChange={setValue}
maxVisible={2}
options={[
{ value: "design", label: "Design" },
{ value: "motion", label: "Motion" },
{ value: "data", label: "Data visualisation" },
{ value: "a11y", label: "Accessibility" },
{ value: "i18n", label: "Internationalisation" },
]}
/>
);
}
Examples
Real screens rather than isolated props. Switch to Code to copy a whole example into your project.
Skills Multiple choices summarised into the trigger once there are more than two.
Skills
Multiple choices summarised into the trigger once there are more than two. preview
Live · keyboard ready
Skills
skills-multiple-choices-summarised-into-the-trigger-once-there-are-more-than-two-.tsxtsx
"use client";
import { useState } from "react";
import { MultiSelect } from "@/components/manicat/multi-select/multi-select";
export function MultiSelectSkillsExample() {
const [value, setValue] = useState(["design", "motion"]);
return (
<div className="w-full max-w-sm">
<MultiSelect
label="Skills"
value={value}
onValueChange={setValue}
maxVisible={2}
options={[
{ value: "design", label: "Design" },
{ value: "motion", label: "Motion" },
{ value: "data", label: "Data visualisation" },
{ value: "a11y", label: "Accessibility" },
{ value: "i18n", label: "Internationalisation" },
]}
/>
</div>
);
}
Keyboard interactions
| Keys | Action |
|---|---|
| Enter / Space | Opens or closes the list; Enter toggles the active option while open. |
| ArrowDown / ArrowUp | Opens the list and moves through enabled options, wrapping. |
| Escape | Closes the list. |
Accessibility
- Trigger has aria-haspopup="listbox", aria-expanded, and aria-labelledby combining the label and a hidden list of selected labels.
- The menu is role="listbox" with aria-multiselectable; options carry aria-selected and aria-disabled.
- Clear button is labelled "Clear selections"; chips are aria-hidden in favor of the spoken summary.
Motion
- Chips open their slot width on a smooth spring and grow in from 0.9 with a blur; the +N count rolls up or down.
- Checks draw in the menu; the menu springs in from slightly above.
- Reduced motion turns every change into a short crossfade with no scale or width travel.
Responsive behavior
- Chips cap at 9rem and ellipsize, and maxVisible limits how many show before +N, so the trigger holds one line.
- The menu spans the field width and has no max height or scroll, so keep option lists short on small screens.
Performance
- Options are not virtualized and the menu does not scroll; for long lists use combobox instead.
- Chips animate slot width and scale per change; fine for a few selections.
Notes for AI
- Use for picking several values from a fixed list in a compact field. Use chip-group when all options should stay visible and tag-input for free-form values.
- Controlled with value and onValueChange or uncontrolled with defaultValue. There is no name prop, so serialize the array yourself for forms.