Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

A multi-select component designed with shadcn/ui

License

NotificationsYou must be signed in to change notification settings

sersavan/shadcn-multi-select-component

Repository files navigation

A powerful and flexible multi-select component built withReact,TypeScript,Tailwind CSS, andshadcn/ui components.

Compatible with: Next.js, Vite, Create React App, and any React environmentthat supports path aliases and shadcn/ui components.

Multi Select DemoTypeScriptTailwind CSS

Star History

Star History Chart

🚀 Features

  • Multiple Variants: Default, secondary, destructive, and inverted styles
  • 🌈Custom Styling: Custom badge colors, icon colors, and gradientbackgrounds
  • 📁Grouped Options: Organize options in groups with headings andseparators
  • 🚫Disabled Options: Mark specific options as disabled and non-selectable
  • 🎨Advanced Animations: Multiple animation types (bounce, pulse, wiggle,fade, slide) for badges and popovers
  • 🔍Search & Filter: Built-in search functionality with keyboard navigation
  • 📊Dashboard Integration: Perfect for analytics dashboards and datavisualization
  • 📈Chart Filtering: Real-time filtering for bar, pie, area, and linecharts
  • 🎯Multi-level Filtering: Primary and secondary filter combinations
  • 📱Responsive Design: Automatic adaptation to mobile, tablet, and desktopscreens
  • 📐Width Constraints: Support for minimum and maximum width settings
  • 📲Mobile-Optimized: Compact mode with touch-friendly interactions
  • 💻Desktop-Enhanced: Full feature set with large displays
  • Accessibility: Full keyboard support, screen reader compatibility, andARIA live regions
  • ⌨️Keyboard Shortcuts: Global hotkeys for navigation and quick actions
  • 🔧Imperative Methods: Programmatic control via ref (reset, clear, focus,get/set values)
  • 🔄Duplicate Handling: Automatic detection and removal of duplicateoptions
  • 📝Form Integration: Seamless integration with React Hook Form andvalidation
  • 🎛️Customizable Behavior: Auto-close on select, width constraints, emptyindicators
  • 🎯TypeScript Support: Fully typed with comprehensive TypeScript support
  • 📦Self-Contained: All accessibility features built-in, no externaldependencies required

📦 Installation

Prerequisites

This component is compatible with any React project but requires proper setup:

  • React environment: Next.js, Vite, Create React App, or any React setup
  • Path aliases: Configure@/ imports in your bundler
  • shadcn/ui: Install and configure shadcn/ui components
  • Tailwind CSS: Setup and configure Tailwind CSS

1. Copy the Component

cp src/components/multi-select.tsx your-project/components/

2. Install Dependencies

npm install react react-domnpm install @radix-ui/react-popover @radix-ui/react-separatornpm install lucide-react class-variance-authority clsx tailwind-merge cmdk

3. Setup shadcn/ui Components

Install the required shadcn/ui components:

npx shadcn@latest add button badge popovercommand separator

4. Configure Path Aliases

For Next.js

Add to yourtsconfig.json orjsconfig.json:

{"compilerOptions": {"baseUrl":".","paths": {"@/*": ["./src/*"]}}}

For Vite

Add to yourvite.config.ts:

import{defineConfig}from"vite";importpathfrom"path";exportdefaultdefineConfig({resolve:{alias:{"@":path.resolve(__dirname,"./src"),},},});

For Webpack/Create React App

You may need to eject or use CRACO to configure path aliases.

5. Setup Utility Function

Ensure you have thecn utility function insrc/lib/utils.ts:

import{typeClassValue,clsx}from"clsx";import{twMerge}from"tailwind-merge";exportfunctioncn(...inputs:ClassValue[]){returntwMerge(clsx(inputs));}

🎯 Quick Start

Basic Usage

import{MultiSelect}from"@/components/multi-select";import{useState}from"react";constoptions=[{value:"react",label:"React"},{value:"vue",label:"Vue.js"},{value:"angular",label:"Angular"},];functionApp(){const[selectedValues,setSelectedValues]=useState<string[]>([]);return(<MultiSelectoptions={options}onValueChange={setSelectedValues}defaultValue={selectedValues}/>);}

Custom Styling

conststyledOptions=[{value:"react",label:"React",icon:ReactIcon,style:{badgeColor:"#61DAFB",iconColor:"#282C34",},},{value:"vue",label:"Vue.js",icon:VueIcon,style:{gradient:"linear-gradient(135deg, #4FC08D 0%, #42B883 100%)",},},];<MultiSelectoptions={styledOptions}onValueChange={setSelected}placeholder="Select with custom styles..."/>;

Next.js Usage

For Next.js projects, you can use the component in client components with the"use client" directive:

"use client";import{MultiSelect}from"@/components/multi-select";import{useState}from"react";constoptions=[{value:"next",label:"Next.js"},{value:"react",label:"React"},{value:"typescript",label:"TypeScript"},];exportdefaultfunctionMyPage(){const[selected,setSelected]=useState<string[]>([]);return(<divclassName="container mx-auto p-4"><h1className="text-2xl font-bold mb-4">Select Technologies</h1><MultiSelectoptions={options}onValueChange={setSelected}placeholder="Select technologies..."variant="secondary"/></div>);}

Grouped Options

constgroupedOptions=[{heading:"Frontend Frameworks",options:[{value:"react",label:"React"},{value:"vue",label:"Vue.js"},{value:"angular",label:"Angular",disabled:true},],},{heading:"Backend Technologies",options:[{value:"node",label:"Node.js"},{value:"python",label:"Python"},],},];<MultiSelectoptions={groupedOptions}onValueChange={setSelected}placeholder="Select from groups..."/>;

📱 Responsive Design

The Multi-Select component includes comprehensive responsive design capabilitiesthat automatically adapt to different screen sizes.

Automatic Responsive Behavior

Enable responsive design with default settings:

<MultiSelectoptions={options}onValueChange={setSelected}responsive={true}// Enable automatic responsive behaviorplaceholder="Responsive component"/>

Default responsive settings:

  • Mobile (< 640px): 2 badges max, compact mode
  • Tablet (640px - 1024px): 4 badges max, normal mode
  • Desktop (> 1024px): 6 badges max, full features

Width Constraints

Control component width with responsive adaptation:

<MultiSelectoptions={options}onValueChange={setSelected}responsive={true}minWidth="200px"maxWidth="400px"placeholder="Constrained width"/>

📊 Dashboard Integration

The Multi-Select component provides powerful integration with analyticsdashboards and data visualization libraries.

Basic Dashboard Filtering

import{MultiSelect}from"@/components/multi-select";import{BarChart,Bar,XAxis,YAxis,ResponsiveContainer}from"recharts";constDashboard=()=>{const[selectedCategories,setSelectedCategories]=useState(["2024"]);constfilteredData=data.filter((item)=>selectedCategories.includes(item.category));return(<divclassName="space-y-4"><MultiSelectoptions={[{value:"2024",label:"2024",icon:CalendarIcon},{value:"2023",label:"2023",icon:CalendarIcon},]}onValueChange={setSelectedCategories}defaultValue={selectedCategories}placeholder="Select time period"responsive={true}/><ResponsiveContainerwidth="100%"height={300}><BarChartdata={filteredData}><XAxisdataKey="name"/><YAxis/><BardataKey="value"fill="#8884d8"/></BarChart></ResponsiveContainer></div>);};

Multi-level Filtering

constAdvancedDashboard=()=>{const[primaryFilters,setPrimaryFilters]=useState(["Performance"]);const[secondaryFilters,setSecondaryFilters]=useState(["Speed"]);return(<divclassName="space-y-4"><divclassName="grid grid-cols-2 gap-4"><MultiSelectoptions={primaryCategories}onValueChange={setPrimaryFilters}placeholder="Primary category"/><MultiSelectoptions={secondaryCategories}onValueChange={setSecondaryFilters}placeholder="Secondary filters"variant="secondary"/></div><ComposedChartdata={filteredData}>{/* Multiple chart types combined */}</ComposedChart></div>);};

📖 Examples

Basic Multi-Select

const[selectedValues,setSelectedValues]=useState<string[]>([]);<MultiSelectoptions={frameworks}onValueChange={setSelectedValues}defaultValue={selectedValues}placeholder="Select frameworks"/>;

With Icons and Custom Styling

const[selectedValues,setSelectedValues]=useState<string[]>([]);<MultiSelectoptions={frameworksWithIcons}onValueChange={setSelectedValues}defaultValue={selectedValues}placeholder="Select technologies"variant="inverted"maxCount={3}modalPopover={true}/>;

Async Data Loading

const[options,setOptions]=useState<Option[]>([]);const[loading,setLoading]=useState(true);useEffect(()=>{loadData().then((data)=>{setOptions(data);setLoading(false);});},[]);<MultiSelectoptions={options}onValueChange={setSelectedValues}defaultValue={selectedValues}placeholder={loading ?"Loading..." :"Select items"}disabled={loading}/>;

📚 API Reference

🎯 Best Practices

  1. Always provide meaningful labels: Use descriptive placeholders andaria-labels
  2. Handle loading states: Show loading indicators when fetching async data
  3. Limit display count: UsemaxCount for large selections to maintain UIclarity
  4. Use modal on mobile: SetmodalPopover={true} for better mobileexperience
  5. Implement search: For large option lists, consider adding searchfunctionality
  6. Provide clear feedback: Use the built-in announcements for screen readers

�📚 API Reference

Props

PropTypeDefaultDescription
optionsMultiSelectOption[] | MultiSelectGroup[]-Array of selectable options or groups
onValueChange(value: string[]) => void-Callback when selection changes
defaultValuestring[][]Initially selected values
placeholderstring"Select options"Placeholder text
variant"default" | "secondary" | "destructive" | "inverted""default"Visual variant
animationnumber0Legacy animation duration in seconds
animationConfigAnimationConfig-Advanced animation configuration
maxCountnumber3Maximum visible selected items
modalPopoverbooleanfalseModal behavior for popover
asChildbooleanfalseRender as child component
classNamestring-Additional CSS classes
hideSelectAllbooleanfalseHide "Select All" option
searchablebooleantrueEnable search functionality
emptyIndicatorReactNode-Custom empty state component
autoSizebooleanfalseAllow component to grow/shrink with content
singleLinebooleanfalseShow badges in single line with scroll
popoverClassNamestring-Custom CSS class for popover content
disabledbooleanfalseDisable the entire component
responsiveboolean | ResponsiveConfigfalseEnable responsive behavior
minWidthstring-Minimum component width
maxWidthstring-Maximum component width
deduplicateOptionsbooleanfalseAutomatically remove duplicate options
resetOnDefaultValueChangebooleantrueReset state when defaultValue changes
closeOnSelectbooleanfalseClose popover after selecting an option

Types

AnimationConfig

interfaceAnimationConfig{badgeAnimation?:"bounce"|"pulse"|"wiggle"|"fade"|"slide"|"none";popoverAnimation?:"scale"|"slide"|"fade"|"flip"|"none";optionHoverAnimation?:"highlight"|"scale"|"glow"|"none";duration?:number;// Animation duration in secondsdelay?:number;// Animation delay in seconds}

MultiSelectRef

interfaceMultiSelectRef{reset:()=>void;// Reset to default valuegetSelectedValues:()=>string[];// Get current selectionsetSelectedValues:(values:string[])=>void;// Set selection programmaticallyclear:()=>void;// Clear all selectionsfocus:()=>void;// Focus the component}

MultiSelectOption

interfaceMultiSelectOption{label:string;// Display textvalue:string;// Unique identifiericon?:React.ComponentType<{// Optional icon componentclassName?:string;}>;disabled?:boolean;// Whether option is disabledstyle?:{// Custom stylingbadgeColor?:string;// Custom badge background coloriconColor?:string;// Custom icon colorgradient?:string;// Gradient background (CSS gradient)};}

MultiSelectGroup

interfaceMultiSelectGroup{heading:string;// Group heading textoptions:MultiSelectOption[];// Options in this group}

♿ Accessibility

The MultiSelect component includes comprehensive accessibility featuresbuilt-in:

ARIA Support

  • ARIA Live Regions: Automatic announcements for screen readers whenselections change
  • Role Attributes: Propercombobox,listbox, andoption roles
  • ARIA Labels: Descriptive labels for all interactive elements
  • ARIA States:aria-expanded,aria-selected,aria-disabled states

Keyboard Navigation

  • Tab: Focus component and navigate between elements
  • Enter/Space: Open dropdown and select options
  • Arrow Keys: Navigate through options
  • Escape: Close dropdown
  • Backspace: Remove last selected item when search is empty

Screen Reader Announcements

The component automatically announces:

  • Number of options selected
  • When dropdown opens/closes
  • Search results count
  • Individual option selection/deselection

Example with Enhanced Accessibility

<MultiSelectoptions={options}onValueChange={setSelected}placeholder="Choose frameworks (accessible)"// Accessibility features are built-in and automaticsearchable={true}aria-label="Framework selection"/>

Manual Accessibility Testing

// The component provides imperative methods for testingconstmultiSelectRef=useRef<MultiSelectRef>(null);// Programmatic focus for testingmultiSelectRef.current?.focus();// Check current selectionconstcurrentValues=multiSelectRef.current?.getSelectedValues();

🎨 Styling Examples

Custom Colors

// Single color badge with custom icon color{value:"react",label:"React",style:{badgeColor:"#61DAFB",iconColor:"#282C34"}}// Gradient badge (icon will be white by default){value:"vue",label:"Vue.js",style:{gradient:"linear-gradient(135deg, #4FC08D 0%, #42B883 100%)"}}// Multiple gradients{value:"angular",label:"Angular",style:{gradient:"linear-gradient(45deg, #DD0031 0%, #C3002F 50%, #FF6B6B 100%)"}}

Brand Colors

constbrandColors={react:{badgeColor:"#61DAFB",iconColor:"#282C34"},vue:{gradient:"linear-gradient(135deg, #4FC08D 0%, #42B883 100%)"},angular:{badgeColor:"#DD0031",iconColor:"#ffffff"},svelte:{gradient:"linear-gradient(135deg, #FF3E00 0%, #FF8A00 100%)"},node:{badgeColor:"#339933",iconColor:"#ffffff"},};

Animation Examples

// Wiggle animation with custom timing<MultiSelectoptions={options}onValueChange={setSelected}animationConfig={{badgeAnimation:"wiggle",duration:0.5,}}/>// Pulse animation with delay<MultiSelectoptions={options}onValueChange={setSelected}animationConfig={{badgeAnimation:"pulse",popoverAnimation:"fade",duration:0.3,delay:0.1,}}/>// Scale animation for popover<MultiSelectoptions={options}onValueChange={setSelected}animationConfig={{badgeAnimation:"slide",popoverAnimation:"scale",duration:0.4,}}/>

🎯 Advanced Examples

Advanced Animations

<MultiSelectoptions={options}onValueChange={setSelected}animationConfig={{badgeAnimation:"wiggle",popoverAnimation:"scale",duration:0.3,delay:0.1,}}placeholder="Animated component"/>

Programmatic Control via Ref

import{useRef}from"react";importtype{MultiSelectRef}from"@/components/multi-select";functionControlledExample(){constmultiSelectRef=useRef<MultiSelectRef>(null);consthandleReset=()=>{multiSelectRef.current?.reset();};consthandleClear=()=>{multiSelectRef.current?.clear();};consthandleSelectAll=()=>{constallValues=options.map((option)=>option.value);multiSelectRef.current?.setSelectedValues(allValues);};consthandleFocus=()=>{multiSelectRef.current?.focus();};return(<divclassName="space-y-4"><MultiSelectref={multiSelectRef}options={options}onValueChange={setSelected}placeholder="Controlled component"/><divclassName="flex gap-2"><buttononClick={handleReset}>Reset</button><buttononClick={handleClear}>Clear</button><buttononClick={handleSelectAll}>Select All</button><buttononClick={handleFocus}>Focus</button></div></div>);}

Auto-close on Select

<MultiSelectoptions={options}onValueChange={setSelected}closeOnSelect={true}placeholder="Closes after each selection"/>

Duplicate Handling

constoptionsWithDuplicates=[{value:"react",label:"React"},{value:"react",label:"React Duplicate"},// Will be handled{value:"vue",label:"Vue.js"},];<MultiSelectoptions={optionsWithDuplicates}onValueChange={setSelected}deduplicateOptions={true}// Automatically removes duplicatesplaceholder="Handles duplicates"/>;

With Form Integration (React Hook Form)

import{useForm,Controller}from"react-hook-form";import{zodResolver}from"@hookform/resolvers/zod";import{z}from"zod";constformSchema=z.object({technologies:z.array(z.string()).min(1,"Select at least one technology"),});functionMyForm(){constform=useForm({resolver:zodResolver(formSchema),defaultValues:{technologies:[]},});return(<formonSubmit={form.handleSubmit(onSubmit)}><Controllercontrol={form.control}name="technologies"render={({ field})=>(<MultiSelectoptions={techOptions}onValueChange={field.onChange}defaultValue={field.value}placeholder="Select technologies..."/>)}/></form>);}

Programmatic Control

functionControlledExample(){const[selected,setSelected]=useState<string[]>([]);constselectRandom=()=>{constrandomItems=options.filter((item)=>!item.disabled).sort(()=>0.5-Math.random()).slice(0,3).map((item)=>item.value);setSelected(randomItems);};return(<div><MultiSelectoptions={options}onValueChange={setSelected}defaultValue={selected}/><buttononClick={selectRandom}>Random Selection</button><buttononClick={()=>setSelected([])}>Clear All</button></div>);}

Custom Empty State

<MultiSelectoptions={options}onValueChange={setSelected}searchable={true}emptyIndicator={<divclassName="flex flex-col items-center p-4"><SearchIconclassName="h-8 w-8 text-muted-foreground mb-2"/><pclassName="text-muted-foreground">No items found</p><pclassName="text-xs text-muted-foreground">Try a different search term</p></div>}/>

Complex Grouped Structure

constcomplexStructure=[{heading:"Frontend Frameworks",options:[{value:"react",label:"React",icon:ReactIcon,style:{badgeColor:"#61DAFB",iconColor:"#282C34"},},{value:"vue",label:"Vue.js",icon:VueIcon,style:{gradient:"linear-gradient(135deg, #4FC08D 0%, #42B883 100%)",},},{value:"angular",label:"Angular",icon:AngularIcon,disabled:true,style:{badgeColor:"#DD0031",iconColor:"#ffffff"},},],},{heading:"State Management",options:[{value:"redux",label:"Redux"},{value:"zustand",label:"Zustand"},{value:"recoil",label:"Recoil",disabled:true},],},];

🎯 Use Cases

  • Technology Stack Selection: Choose programming languages, frameworks,libraries
  • Skill Assessment: Multi-skill selection for profiles or job applications
  • Category Filtering: Filter content by multiple categories
  • Tag Management: Select multiple tags for articles or products
  • Permission Management: Assign multiple roles or permissions
  • Geographic Selection: Choose multiple countries, regions, or locations
  • Product Configuration: Select features, variants, or add-ons
  • Team Assignment: Assign multiple team members to projects

🛠️ Customization

Style Customization

The component uses CSS classes that can be customized via Tailwind CSS. You canoverride styles by passing custom classes:

<MultiSelectclassName="my-custom-class"options={options}onValueChange={setSelected}/>

Custom Variants

Create your own variants by extending themultiSelectVariants:

constcustomVariants=cva("base-classes",{variants:{variant:{// ... existing variantspremium:"bg-gradient-to-r from-purple-500 to-pink-500 text-white",minimal:"bg-transparent border-dashed",},},});

Theme Integration

The component automatically adapts to your theme (light/dark mode) when usingthe shadcn/ui theme provider.

📝 TypeScript Support

The component is fully typed and provides excellent TypeScript support:

// All types are exported for useimporttype{MultiSelectOption,MultiSelectGroup,MultiSelectProps,MultiSelectRef,AnimationConfig,}from"@/components/multi-select";// Type-safe option creationconstcreateOption=(value:string,label:string,options?:Partial<MultiSelectOption>):MultiSelectOption=>({value,label,...options,});// Type-safe event handlersconsthandleChange=(values:string[])=>{// values is automatically typed as string[]console.log("Selected:",values);};

🚀 Getting Started

Clone and Run

# Clone the repositorygit clone https://github.com/sersavan/shadcn-multi-select-component.git# Navigate to the projectcd shadcn-multi-select-component# Install dependenciesnpm install# Start the development servernpm run dev# Open your browser to http://localhost:3000

Project Structure

├── src/│   ├── app/│   │   ├── page.tsx           # Demo page with examples│   │   ├── layout.tsx         # Root layout│   │   └── globals.css        # Global styles│   ├── components/│   │   ├── multi-select.tsx   # Main component│   │   ├── icons.tsx          # Icon components│   │   └── ui/                # shadcn/ui components│   └── lib/│       └── utils.ts           # Utility functions├── components.json            # shadcn/ui config├── tailwind.config.ts         # Tailwind configuration└── package.json              # Dependencies and scripts

🤝 Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

📄 License

This project is licensed under the MIT License - see theLICENSE filefor details.

🙏 Acknowledgments

🚀 Live Demo

Check out the live demo:Multi-Select Component Demo


Made with ❤️ bysersavan

Releases

No releases published

Packages

No packages published

[8]ページ先頭

©2009-2025 Movatter.jp