npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-multiselect-radixui

v1.0.0

Published

A modern, accessible React multi-select component with search functionality and visible selected tabs, built with TypeScript and Tailwind CSS.

Readme

React Multi-Select with Tabs

A modern, accessible React multi-select component with search functionality and visible selected tabs, built with TypeScript and Tailwind CSS.

Features

  • 🎯 Multi-select with visible tags - Selected items appear as removable tags
  • 🔍 Search functionality - Filter options with real-time search
  • ⌨️ Keyboard navigation - Full keyboard support with arrow keys, Enter, Escape
  • 🎨 Tailwind CSS styling - Beautiful, customizable design
  • Accessible - ARIA compliant and screen reader friendly
  • 📱 Responsive - Works great on all screen sizes
  • 🔧 TypeScript - Full TypeScript support with proper types
  • 🎛️ Highly customizable - Extensive customization options

Installation

npm install react-multi-select-tabs

Make sure you have Tailwind CSS installed and configured in your project.

Basic Usage

import React, { useState } from 'react';
import { MultiSelect, Option } from 'react-multi-select-tabs';

const options: Option[] = [
  { value: '1', label: 'Apple' },
  { value: '2', label: 'Banana' },
  { value: '3', label: 'Cherry' },
  { value: '4', label: 'Date' },
  { value: '5', label: 'Elderberry' }
];

function App() {
  const [selectedValues, setSelectedValues] = useState<(string | number)[]>([]);

  return (
    <div className="max-w-md mx-auto p-6">
      <label className="block text-sm font-medium text-gray-700 mb-2">
        Select Fruits
      </label>
      <MultiSelect
        options={options}
        value={selectedValues}
        onChange={setSelectedValues}
        placeholder="Choose your favorite fruits..."
        searchPlaceholder="Search fruits..."
      />
    </div>
  );
}

export default App;

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | options | Option[] | [] | Array of options to display | | value | (string \| number)[] | [] | Currently selected values | | onChange | (values: (string \| number)[]) => void | - | Callback when selection changes | | placeholder | string | "Select options..." | Placeholder when no items selected | | searchPlaceholder | string | "Search..." | Placeholder for search input | | disabled | boolean | false | Disable the entire component | | maxSelectedItems | number | - | Maximum number of items that can be selected | | className | string | "" | Additional CSS classes for container | | dropdownClassName | string | "" | Additional CSS classes for dropdown | | tagClassName | string | "" | Additional CSS classes for selected tags | | searchInputClassName | string | "" | Additional CSS classes for search input | | noOptionsMessage | string | "No options found" | Message when no options match search | | closeOnSelect | boolean | false | Close dropdown after selecting an item | | clearable | boolean | true | Show clear all button | | loading | boolean | false | Show loading state | | loadingMessage | string | "Loading..." | Loading message text |

Option Interface

interface Option {
  value: string | number;
  label: string;
  disabled?: boolean;
}

Advanced Examples

With Maximum Selection Limit

<MultiSelect
  options={options}
  value={selectedValues}
  onChange={setSelectedValues}
  maxSelectedItems={3}
  placeholder="Select up to 3 items"
/>

With Custom Styling

<MultiSelect
  options={options}
  value={selectedValues}
  onChange={setSelectedValues}
  className="w-full"
  tagClassName="bg-green-100 text-green-800"
  dropdownClassName="border-2 border-green-200"
  searchInputClassName="text-lg"
/>

With Loading State

<MultiSelect
  options={options}
  value={selectedValues}
  onChange={setSelectedValues}
  loading={isLoading}
  loadingMessage="Fetching options..."
/>

Keyboard Navigation

  • Arrow Down/Up: Navigate through options
  • Enter: Select highlighted option or open dropdown
  • Escape: Close dropdown
  • Backspace: Remove last selected item when search is empty

Styling

The component uses Tailwind CSS classes and can be customized by:

  1. Overriding CSS classes with the various className props
  2. Using Tailwind's configuration to customize colors and spacing
  3. CSS custom properties for advanced theming

Browser Support

  • Chrome (latest)
  • Firefox (latest)
  • Safari (latest)
  • Edge (latest)

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT License - see the LICENSE file for details.


Now I'll create a demo component to showcase the library:

<lov-write file_path="src/pages/Index.tsx">
import React, { useState } from 'react';
import { MultiSelect } from '../index';
import type { Option } from '../types';

const fruitOptions: Option[] = [
  { value: 'apple', label: 'Apple' },
  { value: 'banana', label: 'Banana' },
  { value: 'cherry', label: 'Cherry' },
  { value: 'date', label: 'Date' },
  { value: 'elderberry', label: 'Elderberry' },
  { value: 'fig', label: 'Fig' },
  { value: 'grape', label: 'Grape' },
  { value: 'honeydew', label: 'Honeydew' },
  { value: 'kiwi', label: 'Kiwi' },
  { value: 'lemon', label: 'Lemon' },
  { value: 'mango', label: 'Mango' },
  { value: 'orange', label: 'Orange' },
  { value: 'papaya', label: 'Papaya' },
  { value: 'quince', label: 'Quince' },
  { value: 'raspberry', label: 'Raspberry' },
  { value: 'strawberry', label: 'Strawberry' },
];

const countryOptions: Option[] = [
  { value: 'us', label: 'United States' },
  { value: 'ca', label: 'Canada' },
  { value: 'mx', label: 'Mexico' },
  { value: 'uk', label: 'United Kingdom' },
  { value: 'fr', label: 'France' },
  { value: 'de', label: 'Germany' },
  { value: 'it', label: 'Italy' },
  { value: 'es', label: 'Spain' },
  { value: 'jp', label: 'Japan' },
  { value: 'au', label: 'Australia' },
];

const Index = () => {
  const [selectedFruits, setSelectedFruits] = useState<(string | number)[]>(['apple', 'banana']);
  const [selectedCountries, setSelectedCountries] = useState<(string | number)[]>([]);
  const [limitedSelection, setLimitedSelection] = useState<(string | number)[]>([]);
  const [loading, setLoading] = useState(false);

  const handleLoadingDemo = () => {
    setLoading(true);
    setTimeout(() => setLoading(false), 2000);
  };

  return (
    <div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 py-12 px-4">
      <div className="max-w-4xl mx-auto">
        {/* Header */}
        <div className="text-center mb-12">
          <h1 className="text-4xl font-bold text-gray-900 mb-4">
            React Multi-Select with Tabs
          </h1>
          <p className="text-xl text-gray-600 max-w-2xl mx-auto">
            A modern, accessible multi-select component with search functionality and visible selected tabs
          </p>
        </div>

        {/* Demo Grid */}
        <div className="grid gap-8 md:grid-cols-2">
          {/* Basic Example */}
          <div className="bg-white rounded-xl shadow-lg p-6">
            <h2 className="text-xl font-semibold text-gray-800 mb-4">Basic Example</h2>
            <div className="space-y-3">
              <label className="block text-sm font-medium text-gray-700">
                Select Your Favorite Fruits
              </label>
              <MultiSelect
                options={fruitOptions}
                value={selectedFruits}
                onChange={setSelectedFruits}
                placeholder="Choose fruits..."
                searchPlaceholder="Search fruits..."
              />
              <div className="text-sm text-gray-500">
                Selected: {selectedFruits.length} items
              </div>
            </div>
          </div>

          {/* Countries Example */}
          <div className="bg-white rounded-xl shadow-lg p-6">
            <h2 className="text-xl font-semibold text-gray-800 mb-4">Countries Selection</h2>
            <div className="space-y-3">
              <label className="block text-sm font-medium text-gray-700">
                Select Countries
              </label>
              <MultiSelect
                options={countryOptions}
                value={selectedCountries}
                onChange={setSelectedCountries}
                placeholder="Choose countries..."
                searchPlaceholder="Search countries..."
                closeOnSelect={true}
              />
              <div className="text-sm text-gray-500">
                Close on select enabled
              </div>
            </div>
          </div>

          {/* Limited Selection */}
          <div className="bg-white rounded-xl shadow-lg p-6">
            <h2 className="text-xl font-semibold text-gray-800 mb-4">Limited Selection</h2>
            <div className="space-y-3">
              <label className="block text-sm font-medium text-gray-700">
                Select Up to 3 Fruits
              </label>
              <MultiSelect
                options={fruitOptions}
                value={limitedSelection}
                onChange={setLimitedSelection}
                maxSelectedItems={3}
                placeholder="Maximum 3 selections..."
                tagClassName="bg-purple-100 text-purple-800"
              />
              <div className="text-sm text-gray-500">
                {limitedSelection.length}/3 selected
              </div>
            </div>
          </div>

          {/* Loading State */}
          <div className="bg-white rounded-xl shadow-lg p-6">
            <h2 className="text-xl font-semibold text-gray-800 mb-4">Loading State</h2>
            <div className="space-y-3">
              <label className="block text-sm font-medium text-gray-700">
                Demo Loading State
              </label>
              <MultiSelect
                options={fruitOptions}
                value={[]}
                onChange={() => {}}
                loading={loading}
                loadingMessage="Fetching options..."
                placeholder="Click button to see loading..."
              />
              <button
                onClick={handleLoadingDemo}
                disabled={loading}
                className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50 transition-colors text-sm"
              >
                {loading ? 'Loading...' : 'Trigger Loading'}
              </button>
            </div>
          </div>
        </div>

        {/* Features Section */}
        <div className="mt-12 bg-white rounded-xl shadow-lg p-8">
          <h2 className="text-2xl font-bold text-gray-800 mb-6">Key Features</h2>
          <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
            <div className="flex items-start space-x-3">
              <div className="flex-shrink-0 w-8 h-8 bg-blue-100 rounded-lg flex items-center justify-center">
                <svg className="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
                </svg>
              </div>
              <div>
                <h3 className="font-semibold text-gray-800">Multi-Select with Tags</h3>
                <p className="text-sm text-gray-600">Selected items appear as removable tags</p>
              </div>
            </div>

            <div className="flex items-start space-x-3">
              <div className="flex-shrink-0 w-8 h-8 bg-green-100 rounded-lg flex items-center justify-center">
                <svg className="w-5 h-5 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
                </svg>
              </div>
              <div>
                <h3 className="font-semibold text-gray-800">Search Functionality</h3>
                <p className="text-sm text-gray-600">Filter options with real-time search</p>
              </div>
            </div>

            <div className="flex items-start space-x-3">
              <div className="flex-shrink-0 w-8 h-8 bg-purple-100 rounded-lg flex items-center justify-center">
                <svg className="w-5 h-5 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 100 4m0-4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 100 4m0-4v2m0-6V4" />
                </svg>
              </div>
              <div>
                <h3 className="font-semibold text-gray-800">Keyboard Navigation</h3>
                <p className="text-sm text-gray-600">Full keyboard support with arrow keys</p>
              </div>
            </div>

            <div className="flex items-start space-x-3">
              <div className="flex-shrink-0 w-8 h-8 bg-yellow-100 rounded-lg flex items-center justify-center">
                <svg className="w-5 h-5 text-yellow-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zM7 3H5a2 2 0 00-2 2v12a4 4 0 004 4h2" />
                </svg>
              </div>
              <div>
                <h3 className="font-semibold text-gray-800">Tailwind Styled</h3>
                <p className="text-sm text-gray-600">Beautiful, customizable design</p>
              </div>
            </div>

            <div className="flex items-start space-x-3">
              <div className="flex-shrink-0 w-8 h-8 bg-red-100 rounded-lg flex items-center justify-center">
                <svg className="w-5 h-5 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
                </svg>
              </div>
              <div>
                <h3 className="font-semibold text-gray-800">Accessible</h3>
                <p className="text-sm text-gray-600">ARIA compliant and screen reader friendly</p>
              </div>
            </div>

            <div className="flex items-start space-x-3">
              <div className="flex-shrink-0 w-8 h-8 bg-indigo-100 rounded-lg flex items-center justify-center">
                <svg className="w-5 h-5 text-indigo-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
                </svg>
              </div>
              <div>
                <h3 className="font-semibold text-gray-800">TypeScript</h3>
                <p className="text-sm text-gray-600">Full TypeScript support with proper types</p>
              </div>
            </div>
          </div>
        </div>

        {/* Installation */}
        <div className="mt-12 bg-gray-900 rounded-xl shadow-lg p-8 text-white">
          <h2 className="text-2xl font-bold mb-4">Installation</h2>
          <div className="bg-gray-800 rounded-lg p-4 font-mono text-sm">
            <span className="text-green-400">npm install</span> react-multi-select-tabs
          </div>
          <p className="mt-4 text-gray-300">
            Make sure you have Tailwind CSS installed and configured in your project.
          </p>
        </div>
      </div>
    </div>
  );
};

export default Index;