Component

Searchable Select

Searchable dropdown with real-time filtering, country flags, and no-results state for large lists.

  • Component
  • Tailwind
  • HTML
  • MIT

Preview

Live component

Interactive preview
Open preview
9:41 100%
devsnips.dev/library/tailwind/components/dropdowns/searchable-select/
fluid · no fixed width 100%
No dependencies Production-ready

Install

Add to your project

terminal
npx devsnips add Tailwind/Components/Dropdowns/searchable-select
registry path: Tailwind/Components/Dropdowns/searchable-select

Source

Implementation

code.html
<!-- Searchable Select -->
<div class="relative" id="searchable-container">
  <label class="block text-sm font-medium text-gray-700 mb-2">Search Country</label>
  
  <div
    id="searchable-trigger"
    class="relative w-full px-4 py-3 text-left bg-white border border-gray-300 rounded-lg cursor-pointer hover:border-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors"
    onclick="toggleSearchable('searchable-menu')"
  >
    <div class="flex items-center justify-between">
      <div id="selected-country" class="flex items-center gap-3">
        <span class="text-gray-400">Type to search...</span>
      </div>
      <svg class="w-5 h-5 text-gray-400 transition-transform" id="searchable-arrow" fill="none" stroke="currentColor" viewBox="0 0 24 24">
        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
      </svg>
    </div>
  </div>

  <!-- Search Input (inside dropdown) -->
  <div
    id="searchable-menu"
    class="hidden absolute z-10 w-full mt-1 bg-white border border-gray-200 rounded-lg shadow-lg overflow-hidden"
  >
    <div class="p-2 border-b border-gray-100">
      <div class="relative">
        <svg class="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
        </svg>
        <input
          type="text"
          id="search-input"
          class="w-full pl-10 pr-4 py-2 text-sm border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
          placeholder="Search countries..."
          oninput="filterCountries()"
        />
      </div>
    </div>

    <!-- Results -->
    <ul id="country-list" class="py-2 max-h-64 overflow-auto">
      <li
        class="country-option px-4 py-2.5 text-gray-700 cursor-pointer hover:bg-blue-50 hover:text-blue-600 transition-colors flex items-center gap-3"
        data-country="United States"
        onclick="selectCountry(this)"
      >
        <span class="text-xl">🇺🇸</span>
        <div>
          <p class="font-medium">United States</p>
          <p class="text-xs text-gray-500">North America</p>
        </div>
      </li>
      
      <li
        class="country-option px-4 py-2.5 text-gray-700 cursor-pointer hover:bg-blue-50 hover:text-blue-600 transition-colors flex items-center gap-3"
        data-country="United Kingdom"
        onclick="selectCountry(this)"
      >
        <span class="text-xl">🇬🇧</span>
        <div>
          <p class="font-medium">United Kingdom</p>
          <p class="text-xs text-gray-500">Europe</p>
        </div>
      </li>
      
      <li
        class="country-option px-4 py-2.5 text-gray-700 cursor-pointer hover:bg-blue-50 hover:text-blue-600 transition-colors flex items-center gap-3"
        data-country="Germany"
        onclick="selectCountry(this)"
      >
        <span class="text-xl">🇩🇪</span>
        <div>
          <p class="font-medium">Germany</p>
          <p class="text-xs text-gray-500">Europe</p>
        </div>
      </li>
      
      <li
        class="country-option px-4 py-2.5 text-gray-700 cursor-pointer hover:bg-blue-50 hover:text-blue-600 transition-colors flex items-center gap-3"
        data-country="France"
        onclick="selectCountry(this)"
      >
        <span class="text-xl">🇫🇷</span>
        <div>
          <p class="font-medium">France</p>
          <p class="text-xs text-gray-500">Europe</p>
        </div>
      </li>
      
      <li
        class="country-option px-4 py-2.5 text-gray-700 cursor-pointer hover:bg-blue-50 hover:text-blue-600 transition-colors flex items-center gap-3"
        data-country="Japan"
        onclick="selectCountry(this)"
      >
        <span class="text-xl">🇯🇵</span>
        <div>
          <p class="font-medium">Japan</p>
          <p class="text-xs text-gray-500">Asia</p>
        </div>
      </li>
      
      <li
        class="country-option px-4 py-2.5 text-gray-700 cursor-pointer hover:bg-blue-50 hover:text-blue-600 transition-colors flex items-center gap-3"
        data-country="Australia"
        onclick="selectCountry(this)"
      >
        <span class="text-xl">🇦🇺</span>
        <div>
          <p class="font-medium">Australia</p>
          <p class="text-xs text-gray-500">Oceania</p>
        </div>
      </li>
      
      <li
        class="country-option px-4 py-2.5 text-gray-700 cursor-pointer hover:bg-blue-50 hover:text-blue-600 transition-colors flex items-center gap-3"
        data-country="Brazil"
        onclick="selectCountry(this)"
      >
        <span class="text-xl">🇧🇷</span>
        <div>
          <p class="font-medium">Brazil</p>
          <p class="text-xs text-gray-500">South America</p>
        </div>
      </li>
      
      <li
        class="country-option px-4 py-2.5 text-gray-700 cursor-pointer hover:bg-blue-50 hover:text-blue-600 transition-colors flex items-center gap-3"
        data-country="Canada"
        onclick="selectCountry(this)"
      >
        <span class="text-xl">🇨🇦</span>
        <div>
          <p class="font-medium">Canada</p>
          <p class="text-xs text-gray-500">North America</p>
        </div>
      </li>
    </ul>

    <!-- No Results -->
    <div id="no-results" class="hidden py-8 text-center">
      <svg class="w-12 h-12 text-gray-300 mx-auto mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
      </svg>
      <p class="text-gray-500">No countries found</p>
    </div>
  </div>
</div>

<script>
  function toggleSearchable(id) {
    const menu = document.getElementById(id);
    const arrow = document.getElementById('searchable-arrow');
    menu.classList.toggle('hidden');
    arrow.classList.toggle('rotate-180');
    
    if (!menu.classList.contains('hidden')) {
      document.getElementById('search-input').focus();
    }
  }

  function filterCountries() {
    const input = document.getElementById('search-input').value.toLowerCase();
    const options = document.querySelectorAll('.country-option');
    let hasResults = false;

    options.forEach(option => {
      const country = option.getAttribute('data-country').toLowerCase();
      if (country.includes(input)) {
        option.classList.remove('hidden');
        hasResults = true;
      } else {
        option.classList.add('hidden');
      }
    });

    document.getElementById('no-results').classList.toggle('hidden', hasResults);
  }

  function selectCountry(element) {
    const country = element.getAttribute('data-country');
    const selectedDisplay = document.getElementById('selected-country');
    
    selectedDisplay.innerHTML = element.innerHTML;
    selectedDisplay.classList.remove('text-gray-400');
    
    document.querySelectorAll('.country-option').forEach(opt => {
      opt.classList.remove('bg-blue-50', 'text-blue-600');
    });
    element.classList.add('bg-blue-50', 'text-blue-600');
    
    toggleSearchable('searchable-menu');
  }

  document.addEventListener('click', function(event) {
    const container = document.getElementById('searchable-container');
    if (!container.contains(event.target)) {
      document.getElementById('searchable-menu').classList.add('hidden');
      document.getElementById('searchable-arrow').classList.remove('rotate-180');
    }
  });
</script>
201 lines UTF-8 · LF · Spaces: 2

AI-ready

Available to your agent.

This component is reachable through the DevSnips CLI, MCP server and Skills integrations — one registry, one resource path.

Resource path

devsnips://components/dropdowns/searchable-select

Continue browsing

View all