March 7, 2025
O. Wolfson
Multi-select dropdowns are crucial for user-friendly forms, allowing users to pick multiple options efficiently. ShadCN provides excellent UI components, but lacks a built-in multi-select component. In this guide, we'll build a custom multi-select dropdown using ShadCN's Popover, Command, and Badge components.
Before we begin, ensure you have ShadCN installed in your Next.js project.
shnpx shadcn-ui@latest init # Initialize ShadCN if not installed
npx shadcn-ui@latest add popover command button badge
This installs the required components: Popover, Command, Button, and Badge.
MultiSelect.tsx ComponentInside your components/ui directory, create a new file MultiSelect.tsx and add the following code:
tsx"use client";
import { useState } from "react";
import {
Popover,
PopoverTrigger,
PopoverContent,
} from "@/components/ui/popover";
import {
Command,
CommandInput,
CommandList,
CommandItem,
CommandEmpty,
} from "@/components/ui/command";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Check, ChevronsUpDown, X } from "lucide-react";
interface Option {
value: string;
label: string;
}
interface MultiSelectProps {
options: Option[];
selectedValues: string[];
setSelectedValues: (values: string[]) => void;
placeholder?: string;
}
const MultiSelect: React.FC<MultiSelectProps> = ({
options,
selectedValues,
setSelectedValues,
placeholder,
}) => {
const [open, setOpen] = useState(false);
const [inputValue, setInputValue] = useState("");
const filteredOptions = options.filter((option) =>
option.label.toLowerCase().includes(inputValue.toLowerCase())
);
const toggleSelection = (value: string) => {
if (selectedValues.includes(value)) {
setSelectedValues(selectedValues.filter((item) => item !== value));
} else {
setSelectedValues([...selectedValues, value]);
}
};
const removeSelected = (value: string) => {
setSelectedValues(selectedValues.filter((item) => item !== value));
};
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
className="flex justify-between px-2 pb-2 items-center h-full min-w-[200px]"
variant="outline"
>
<div className="flex gap-1 flex-wrap">
{selectedValues.length > 0 ? (
selectedValues.map((val, index) => (
<Badge
key={val}
className="flex items-center gap-1 px-2 py-1 bg-gray-200 text-black dark:bg-gray-700 dark:text-white rounded-md"
>
{options.find((opt) => opt.value === val)?.label}
<div
onClick={(e) => {
e.stopPropagation();
removeSelected(val);
}}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.stopPropagation();
removeSelected(val);
}
}}
className="ml-1 text-red-500 hover:text-red-700 cursor-pointer"
>
<X className="h-3 w-3" />
</div>
</Badge>
))
) : (
<span className="text-gray-500">
{placeholder || "Select options..."}
</span>
)}
</div>
<ChevronsUpDown = />
{filteredOptions.length === 0 ? (
No options found.
) : (
filteredOptions.map((option) => {
const isSelected = selectedValues.includes(option.value);
return (
toggleSelection(option.value)}
>
{option.label}
);
})
)}
);
};
export default MultiSelect;
Now that we have created the multi-select component, let's use it in a form.
tsximport React, { useState } from "react";
import MultiSelect from "@/components/ui/MultiSelect";
const options = [
{ value: "react", label: "React" },
{ value: "nextjs", label: "Next.js" },
{ value: "vue", label: "Vue.js" },
{ value: "angular", label: "Angular" },
];
export default function MultiSelectExample() {
const [selectedCategories, setSelectedCategories] = useState<string[]>([]);
return (
<div className="p-4">
<MultiSelect
options={options}
selectedValues={selectedCategories}
setSelectedValues={setSelectedCategories}
placeholder="Select frameworks..."
/>
<p className="mt-4">Selected: {selectedCategories.join(", ")}</p>
</div>
);
}
By using ShadCN components, we successfully built a fully functional, accessible multi-select dropdown with search functionality. This approach allows for highly customizable and reusable components in your Next.js app.
✅ Searchable dropdown with real-time filtering
✅ Allows multiple selections
✅ Displays selected items as badges with remove buttons
✅ Styled using ShadCN components for seamless UI integration
Now you have a powerful multi-select dropdown component ready to use!