JSFiddle - React, Tailwind, and code Playground

by Imri Paloja

HTML

<select id="native-select" name="country" hidden>
  <option value="">Select country</option>
  <option value="us">USA</option>
  <option value="ca">Canada</option>
  <option value="uk">United Kingdom</option>
  <option value="fr">France</option>
</select>

<div class="select" id="custom-select" tabindex="0">
  <div class="select-trigger">Select country</div>
  <div class="select-dropdown">
    <input type="text" class="select-search" placeholder="Search..." />
    <ul class="select-options"></ul>
  </div>
</div>

CSS

.select {
  width: 260px;
  position: relative;
  font-family: system-ui, sans-serif;
}

.select-trigger {
  padding: 10px 12px;
  border: 1px solid #ccc;
  border-radius: 6px;
  cursor: pointer;
  background: white;
}

.select.open .select-trigger {
  border-color: #4f46e5;
}

.select-dropdown {
  display: none;
  position: absolute;
  width: 100%;
  margin-top: 4px;
  background: white;
  border: 1px solid #ccc;
  border-radius: 6px;
  max-height: 220px;
  overflow: auto;
  z-index: 10;
}

.select.open .select-dropdown {
  display: block;
}

.select-search {
  width: 100%;
  padding: 8px;
  border: none;
  border-bottom: 1px solid #eee;
  outline: none;
}

.select-options li {
  padding: 8px 12px;
  cursor: pointer;
}

.select-options li:hover,
.select-options li.active {
  background: #eef2ff;
}

JavaScript

const nativeSelect = document.getElementById('native-select');
const custom = document.getElementById('custom-select');
const trigger = custom.querySelector('.select-trigger');
const dropdown = custom.querySelector('.select-dropdown');
const search = custom.querySelector('.select-search');
const list = custom.querySelector('.select-options');

const options = [...nativeSelect.options].slice(1);

function render(filter = '') {
  list.innerHTML = '';
  options
    .filter((o) => o.text.toLowerCase().includes(filter.toLowerCase()))
    .forEach((o) => {
      const li = document.createElement('li');
      li.textContent = o.text;
      li.onclick = () => selectOption(o);
      list.appendChild(li);
    });
}

function selectOption(option) {
  nativeSelect.value = option.value;
  trigger.textContent = option.text;
  custom.classList.remove('open');
}

trigger.onclick = () => {
  custom.classList.toggle('open');
  search.focus();
};

search.oninput = (e) => render(e.target.value);

document.addEventListener('click', (e) => {
  if (!custom.contains(e.target)) custom.classList.remove('open');
});

render();