Build a Reusable Multi-Select Dropdown in React with ShadCN & React Hook Form

image downloaded from here
Description
In this blog, we will walk through creating a reusable multi-select dropdown component in React using ShadCN’s UI library and integrating it with react-hook-form
for seamless form handling. This component will support dynamic options and validations, making it perfect for any project requiring multiple selections.
Why This Component?
When building forms, having reusable components that integrate well with validation libraries like react-hook-form
is crucial. By the end of this guide, you’ll have a versatile multi-select dropdown that:
Supports dynamic options.
Works seamlessly with
react-hook-form
.Integrates with validation schemas like Zod.
Step 1: Setup Your Project
Before starting, make sure your project is set up with React, react-hook-form
, and ShadCN components. If not, you can install the required dependencies:
npm install react-hook-form zod shadcn-ui
Step 2: Define the Options Format
Let’s assume your options look like this:
const categories = [
{ name: 'JavaScript', id: 'cm4yeqerr0002v5lgvzkr1v35', slug: 'javascript' },
{ name: 'Python', id: 'cm4yeqeve0009v5lgw7rmcvo7', slug: 'python' },
{ name: 'Ruby', id: 'cm4yeqeve0009v5lgw7rmcvo9', slug: 'ruby' },
];
Each category has a name
, id
, and slug
. We’ll use the id
to track selections.
Step 3: Create the Multi-Select Dropdown Component
Here is the implementation of the MultiSelectDropdown
component:
"use client";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
const MultiSelectDropdown = ({
options,
value,
onChange,
placeholder = "Select options",
}) => {
const handleSelect = (option) => {
const isSelected = value.includes(option.id);
if (isSelected) {
onChange(value.filter((id) => id !== option.id));
} else {
onChange([...value, option.id]);
}
};
const handleClearAll = () => {
onChange([]);
};
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" className="w-full justify-between" asChild>
{value.length > 0 ? (
<div className="flex flex-wrap gap-1">
{value.map((id) => {
const selectedOption = options.find(
(option) => option.id === id
);
return (
<Badge key={id} className="flex items-center gap-2">
{selectedOption?.name}
<button
onClick={(e) => {
e.stopPropagation();
// handleSelect(selectedOption);
onChange(value.filter((valueId) => valueId !== id));
}}
>
✕
</button>
</Badge>
);
})}
{/* <Button
onClick={handleClearAll}
className="cursor-pointer text-red-500"
>
Clear All
</Button> */}
</div>
) : (
<span className="text-gray-500">{placeholder}</span>
)}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-full max-h-60 overflow-auto">
{options.map((option) => (
<DropdownMenuItem
key={option.id}
onClick={() => handleSelect(option)}
className={`cursor-pointer ${
value.includes(option.id) ? "bg-gray-500" : ""
}`}
>
<span>{option.name}</span>
</DropdownMenuItem>
))}
{value.length > 0 && (
<DropdownMenuItem
onClick={handleClearAll}
className="cursor-pointer text-red-500"
>
Clear All
</DropdownMenuItem>
)}
</DropdownMenuContent>
</DropdownMenu>
);
};
export default MultiSelectDropdown;
Step 4: Integrate with React Hook Form
Here’s how you can integrate the dropdown component into your form:
Form Setup
"use client";
import React from "react";
import { zodResolver } from "@hookform/resolvers/zod";
import { Button } from "@/components/ui/button";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { z } from "zod";
import { useForm } from "react-hook-form";
import MultiSelectDropdown from "../ui/multi-select";
const formSchema = z.object({
categories: z
.array(z.string())
.min(1, { message: "please atleast select a value" }),
});
function CourseMainForm({ categories }) {
const form = useForm({
resolver: zodResolver(formSchema),
defaultValues: {
categories: []
},
});
const onSubmit = async (data) => {
console.log(data);
};
return (
<div>
<h1>Fill all details for course </h1>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className=" flex flex-col gap-4"
>
<FormField
control={form.control}
name="categories"
render={({ field }) => (
<FormItem>
<FormLabel>categories</FormLabel>
<FormControl>
<MultiSelectDropdown
options={categories} // Array of options
value={field.value} // Current value from react-hook-form
onChange={field.onChange} // Update form value
placeholder="Select categories"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Submit</Button>
</form>
</Form>
</div>
);
}
export default CourseMainForm;
Step 5: Adding Default Values
If you want to set default selected values, ensure your useForm
default values include valid IDs for the categories field:
defaultValues: {
categories: ["cm4yeqeve0009v5lgw7rmcvo7"],
}
Conclusion
With this implementation, you now have a reusable multi-select dropdown component that integrates smoothly with react-hook-form
. This component can be used across various forms and adapted for different types of data. Feel free to customize and expand its functionality to suit your needs.
💥 Did you find this blog helpful? 💥
If you enjoyed this post, please clap and follow for more insights on web development and Next.js! Your support helps me continue sharing useful content to enhance your development journey. 🚀
Written by Sagar Sangwan
👨💻 Programmer | ✈️ Love Traveling | 🍳 Enjoy Cooking | Building cool tech and exploring the world!
View more blogs by me CLICK HERE
Loading related blogs...
SUBSCRIBE to Newsletter
In this newsletter we provide latest news about technology, business and startup ideas. Hope you like it.