Select

Example
x-model:

<div x-data="{ model: [] }" , class="flex flex-col items-center">
  <div
    x-data="select"
    :data-items="[
      { text: 'spanish', value: 'spanish' },
      { text: 'french', value: 'french' },
      { text: 'portuguese', value: 'portuguese' },
      { text: 'german', value: 'german' },
      { text: 'japanese', value: 'japanese' },
      { text: 'korean', value: 'korean' },
      { text: 'italian', value: 'italian' },
      { text: 'polish', value: 'polish' },
      { text: 'greek', value: 'greek'},
      { text: 'swedish', value: 'swedish' },
      { text: 'norwegian', value: 'norwegian' },
      { text: 'danish', value: 'danish' },
      { text: 'finnish', value: 'finnish' },
      { text: 'thai', value: 'thai' },
    ]"
    x-model="model"
    class="w-[340px]"
  >
    <div
      x-bind="trigger"
      x-data="input"
      class="flex flex-1 items-center rounded-sm border px-3 py-2 outline-hidden transition-shadow duration-200 focus-within:ring-3 focus:outline-hidden"
      class-default="border-gray-300 bg-white focus-within:border-gray-400 focus-within:ring-primary-200 dark:border-dark-600 dark:bg-dark-800 dark:text-text-300 dark:focus-within:ring-primary-300"
      class-valid="border-success-300 bg-white text-success-600 focus-within:ring-success-200 dark:border-success-400 dark:bg-dark-800 dark:text-success-600 dark:focus-within:ring-success-300"
      class-invalid="border-danger-300 bg-white text-danger-600 focus-within:ring-danger-200 dark:border-danger-400 dark:bg-dark-800 dark:text-danger-600 dark:focus-within:ring-danger-300"
    >
      <div data-icon class="mr-3 empty:hidden"></div>
      <div data-prepend class="mr-3 empty:hidden"></div>
      <div class="mr-3 flex flex-1 flex-wrap">
        <template x-for="selectedItem in getSelected()">
          <span
            x-text="selectedItem.text"
            class="mr-1 after:content-[','] last-of-type:mr-2 last-of-type:after:content-none"
          ></span>
        </template>
        <input
          x-bind="input"
          type="text"
          class="w-full min-w-0 flex-1 border-0 bg-transparent p-0 outline-hidden focus:min-w-0 focus:outline-hidden"
          readonly
        />
      </div>
      <div data-append class="mr-3 empty:hidden"></div>
      <div class="flex items-center gap-x-2">
        <div x-bind="loader">
          <svg
            viewBox="25 25 50 50"
            fill="none"
            class="h-5 w-5 animate-spinner-rotate"
          >
            <circle
              cx="50"
              cy="50"
              r="20"
              stroke="currentColor"
              stroke-width="4"
              stroke-miterlimit="10"
              stroke-linecap="round"
              class="animate-spinner-dash"
            />
          </svg>
        </div>
        <button
          x-bind="clearButton"
          @click="clearSelection()"
          class="flex items-center"
        >
          <svg
            xmlns="http://www.w3.org/2000/svg"
            width="16"
            height="16"
            fill="currentColor"
            class="pointer-events-none h-5 w-5 opacity-70"
            viewBox="0 0 16 16"
          >
            <path
              d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"
            />
          </svg>
        </button>
        <button>
          <svg
            xmlns="http://www.w3.org/2000/svg"
            fill="currentColor"
            viewBox="0 0 512 512"
            class="h-4 w-4 rotate-180 transform duration-200"
            :class="{ 'rotate-0!': isOpen }"
          >
            <path
              d="M233.4 105.4c12.5-12.5 32.8-12.5 45.3 0l192 192c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L256 173.3 86.6 342.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l192-192z"
            />
          </svg>
        </button>
      </div>
    </div>
    <div
      x-bind="menu"
      class="oveflow-y-auto z-50 max-h-[400px] overflow-x-hidden rounded-b-md bg-white shadow-md dark:bg-dark-800"
      x-alt-transition='{
        "enter": ["opacity-0 scale-y-[0.5]", "transition ease-out duration-100 origin-top", "opacity-100"],
        "leave": ["opacity-100", "transition ease-in duration-100 origin-top", "opacity-0 scale-y-[0.5]"]
      }'
    >
      <template x-if="getItems().length === 0">
        <div class="px-6 py-4 text-left text-text-800 dark:text-text-300">
          No items available.
        </div>
      </template>
      <template x-for="(item, index) in getItems()" :key="item.value">
        <button
          x-bind="option"
          class="w-full cursor-pointer scroll-mt-20 scroll-mb-20 px-6 py-4 text-left ring-secondary-400 transition-colors ring-inset focus:ring-2 focus:outline-hidden"
          class-default="text-text-800 hover:bg-secondary-100 focus:bg-secondary-200 dark:text-text-300 dark:hover:bg-dark-600 dark:focus:bg-dark-600"
          class-selected="bg-secondary-200 focus:bg-secondary-300 dark:bg-dark-600 dark:focus:bg-dark-700"
        >
          <span x-text="item.text"></span>
        </button>
      </template>
    </div>
  </div>
  <div class="mx-auto mt-4 w-max font-mono text-lg">
    x-model: <span x-text="JSON.stringify(model)"></span>
  </div>
</div>

Usage

<script defer src="https://cdn.jsdelivr.net/npm/litewind-alpine@0.x.x/plugins/use-floating/dist/cdn.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/litewind-alpine@0.x.x/components/select/dist/cdn.min.js"></script>

The data for the component is provided by the select function in the x-data directive and the props in the data-* attributes.

The select component is based on the input component, so you can use any of its props or additional elements such as a clear button, icon, loader, and more. See the documentation for the input component here.

Props

data-items
[] Array

Array of items to select. This can be an array of strings or an array of objects. The objects should have at least two properties: a text that will be displayed in the menu and a value. This prop is optional, items can be assigned directly to the items property.


data-item-text
text String

Name of the property that holds the displayed text of the item.


data-item-value
value String

Name of the property that holds the value of the item.


data-multiple
false Boolean

Allows selecting multiple values.


data-clearable
false Boolean

Adds an X button that resets model to the default value.


data-use-loader
false Boolean

Enable or disable the spinner in the input.


data-is-loading
false Boolean

If true displays a spinner in the input. Spinner should be first enabled in the data-use-loader prop.


data-placeholder
empty string String

Sets the placeholder of the input element.

x-model

x-model
[] Array

The type of the x-model is an array for single and multiple modes. The values in the array are the value fields of the selected options.

Events

open-selectmenu

Event dispatched after opening select menu.


scroll-to-bottom

Event dispatched after menu is scrolled to the bottom.

Select multiple values

The data-multiple prop enables the selection of multiple values.

Example
x-model:

<div x-data="{ model: [] }" , class="flex flex-col items-center">
  <div
    x-data="select"
    :data-items="[
      { text: 'spanish', value: 'spanish' },
      { text: 'french', value: 'french' },
      { text: 'portuguese', value: 'portuguese' },
      { text: 'german', value: 'german' },
      { text: 'japanese', value: 'japanese' },
      { text: 'korean', value: 'korean' },
      { text: 'italian', value: 'italian' },
      { text: 'polish', value: 'polish' },
      { text: 'greek', value: 'greek'},
      { text: 'swedish', value: 'swedish' },
      { text: 'norwegian', value: 'norwegian' },
      { text: 'danish', value: 'danish' },
      { text: 'spanish', value: 'spanish' },
      { text: 'finnish', value: 'finnish' },
      { text: 'thai', value: 'thai' },
    ]"
    data-multiple="true"
    x-model="model"
    class="w-[340px]"
  >
    <div
      x-bind="trigger"
      x-data="input"
      class="flex flex-1 items-center rounded-sm border px-3 py-2 outline-hidden transition-shadow duration-200 focus-within:ring-3 focus:outline-hidden"
      class-default="border-gray-300 bg-white focus-within:border-gray-400 focus-within:ring-primary-200 dark:border-dark-600 dark:bg-dark-800 dark:text-text-300 dark:focus-within:ring-primary-300"
      class-valid="border-success-300 bg-white text-success-600 focus-within:ring-success-200 dark:border-success-400 dark:bg-dark-800 dark:text-success-600 dark:focus-within:ring-success-300"
      class-invalid="border-danger-300 bg-white text-danger-600 focus-within:ring-danger-200 dark:border-danger-400 dark:bg-dark-800 dark:text-danger-600 dark:focus-within:ring-danger-300"
    >
      <div data-icon class="mr-3 empty:hidden"></div>
      <div data-prepend class="mr-3 empty:hidden"></div>
      <div class="mr-3 flex flex-1 flex-wrap">
        <template x-for="selectedItem in getSelected()">
          <span
            x-text="selectedItem.text"
            class="mr-1 after:content-[','] last-of-type:mr-2 last-of-type:after:content-none"
          ></span>
        </template>
        <input
          x-bind="input"
          type="text"
          class="w-full min-w-0 flex-1 border-0 bg-transparent p-0 outline-hidden focus:min-w-0 focus:outline-hidden"
          readonly
        />
      </div>
      <div data-append class="mr-3 empty:hidden"></div>
      <div class="flex items-center gap-x-2">
        <div x-bind="loader">
          <svg
            viewBox="25 25 50 50"
            fill="none"
            class="h-5 w-5 animate-spinner-rotate"
          >
            <circle
              cx="50"
              cy="50"
              r="20"
              stroke="currentColor"
              stroke-width="4"
              stroke-miterlimit="10"
              stroke-linecap="round"
              class="animate-spinner-dash"
            />
          </svg>
        </div>
        <button
          x-bind="clearButton"
          @click="clearSelection()"
          class="flex items-center"
        >
          <svg
            xmlns="http://www.w3.org/2000/svg"
            width="16"
            height="16"
            fill="currentColor"
            class="pointer-events-none h-5 w-5 opacity-70"
            viewBox="0 0 16 16"
          >
            <path
              d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"
            />
          </svg>
        </button>
        <button>
          <svg
            xmlns="http://www.w3.org/2000/svg"
            fill="currentColor"
            viewBox="0 0 512 512"
            class="h-4 w-4 rotate-180 transform duration-200"
            :class="{ 'rotate-0!': isOpen }"
          >
            <path
              d="M233.4 105.4c12.5-12.5 32.8-12.5 45.3 0l192 192c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L256 173.3 86.6 342.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l192-192z"
            />
          </svg>
        </button>
      </div>
    </div>
    <div
      x-bind="menu"
      class="oveflow-y-auto z-50 max-h-[400px] overflow-x-hidden rounded-b-md bg-white shadow-md dark:bg-dark-800"
      x-alt-transition='{
        "enter": ["opacity-0 scale-y-[0.5]", "transition ease-out duration-100 origin-top", "opacity-100"],
        "leave": ["opacity-100", "transition ease-in duration-100 origin-top", "opacity-0 scale-y-[0.5]"]
      }'
    >
      <template x-if="getItems().length === 0">
        <div class="px-6 py-4 text-left text-text-800 dark:text-text-300">
          No items available.
        </div>
      </template>
      <template x-for="(item, index) in getItems()">
        <button
          x-bind="option"
          class="w-full cursor-pointer scroll-mt-20 scroll-mb-20 px-6 py-4 text-left ring-secondary-400 transition-colors ring-inset focus:ring-2 focus:outline-hidden"
          class-default="text-text-800 hover:bg-secondary-100 focus:bg-secondary-200 dark:text-text-300 dark:hover:bg-dark-600 dark:focus:bg-dark-600"
          class-selected="bg-secondary-200 focus:bg-secondary-300 dark:bg-dark-600 dark:focus:bg-dark-700"
        >
          <span x-text="item.text"></span>
        </button>
      </template>
    </div>
  </div>
  <div class="mx-auto mt-4 w-max font-mono text-lg">
    x-model: <span x-text="JSON.stringify(model)"></span>
  </div>
</div>

Async items

To load items from a remote resource, simply assign them to the items property. In the example below, the items are fetched when the select menu is opened.

Example
x-model:

<div
  x-data="{ model: [], isLoading: false }"
  class="flex flex-col items-center"
>
  <div
    x-data="select"
    data-item-text="name"
    data-item-value="id"
    data-use-loader="true"
    :data-is-loading="isLoading"
    @open-selectmenu="() => {
      isLoading = true
      fetch('https://jsonplaceholder.typicode.com/users')
        .then(res => res.json())
        .then(json => {
          items = json
          isLoading = false
        })
      }"
    x-model="model"
    class="w-[340px]"
  >
    <div
      x-bind="trigger"
      x-data="input"
      class="flex flex-1 items-center rounded-sm border px-3 py-2 outline-hidden transition-shadow duration-200 focus-within:ring-3 focus:outline-hidden"
      class-default="border-gray-300 bg-white focus-within:border-gray-400 focus-within:ring-primary-200 dark:border-dark-600 dark:bg-dark-800 dark:text-text-300 dark:focus-within:ring-primary-300"
      class-valid="border-success-300 bg-white text-success-600 focus-within:ring-success-200 dark:border-success-400 dark:bg-dark-800 dark:text-success-600 dark:focus-within:ring-success-300"
      class-invalid="border-danger-300 bg-white text-danger-600 focus-within:ring-danger-200 dark:border-danger-400 dark:bg-dark-800 dark:text-danger-600 dark:focus-within:ring-danger-300"
    >
      <div data-icon class="mr-3 empty:hidden"></div>
      <div data-prepend class="mr-3 empty:hidden"></div>
      <div class="mr-3 flex flex-1 flex-wrap">
        <template x-for="selectedItem in getSelected()">
          <span
            x-text="selectedItem.text"
            class="mr-1 after:content-[','] last-of-type:mr-2 last-of-type:after:content-none"
          ></span>
        </template>
        <input
          x-bind="input"
          type="text"
          class="w-full min-w-0 flex-1 border-0 bg-transparent p-0 outline-hidden focus:min-w-0 focus:outline-hidden"
          readonly
        />
      </div>
      <div data-append class="mr-3 empty:hidden"></div>
      <div class="flex items-center gap-x-2">
        <div x-bind="loader">
          <svg
            viewBox="25 25 50 50"
            fill="none"
            class="h-5 w-5 animate-spinner-rotate"
          >
            <circle
              cx="50"
              cy="50"
              r="20"
              stroke="currentColor"
              stroke-width="4"
              stroke-miterlimit="10"
              stroke-linecap="round"
              class="animate-spinner-dash"
            />
          </svg>
        </div>
        <button
          x-bind="clearButton"
          @click="clearSelection()"
          class="flex items-center"
        >
          <svg
            xmlns="http://www.w3.org/2000/svg"
            width="16"
            height="16"
            fill="currentColor"
            class="pointer-events-none h-5 w-5 opacity-70"
            viewBox="0 0 16 16"
          >
            <path
              d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"
            />
          </svg>
        </button>
        <button>
          <svg
            xmlns="http://www.w3.org/2000/svg"
            fill="currentColor"
            viewBox="0 0 512 512"
            class="h-4 w-4 rotate-180 transform duration-200"
            :class="{ 'rotate-0!': isOpen }"
          >
            <path
              d="M233.4 105.4c12.5-12.5 32.8-12.5 45.3 0l192 192c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L256 173.3 86.6 342.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l192-192z"
            />
          </svg>
        </button>
      </div>
    </div>
    <div
      x-bind="menu"
      class="oveflow-y-auto z-50 max-h-[400px] overflow-x-hidden rounded-b-md bg-white shadow-md dark:bg-dark-800"
      x-alt-transition='{
        "enter": ["opacity-0 scale-y-[0.5]", "transition ease-out duration-100 origin-top", "opacity-100"],
        "leave": ["opacity-100", "transition ease-in duration-100 origin-top", "opacity-0 scale-y-[0.5]"]
      }'
    >
      <template x-if="getItems().length === 0">
        <div class="px-6 py-4 text-left text-text-800 dark:text-text-300">
          No items available.
        </div>
      </template>
      <template x-for="(item, index) in getItems()" :key="item.value">
        <button
          x-bind="option"
          class="w-full cursor-pointer scroll-mt-20 scroll-mb-20 px-6 py-4 text-left ring-secondary-400 transition-colors ring-inset focus:ring-2 focus:outline-hidden"
          class-default="text-text-800 hover:bg-secondary-100 focus:bg-secondary-200 dark:text-text-300 dark:hover:bg-dark-600 dark:focus:bg-dark-600"
          class-selected="bg-secondary-200 focus:bg-secondary-300 dark:bg-dark-600 dark:focus:bg-dark-700"
        >
          <span x-text="item.text"></span>
        </button>
      </template>
    </div>
  </div>
  <div class="mx-auto mt-4 w-max font-mono text-lg">
    x-model: <span x-text="JSON.stringify(model)"></span>
  </div>
</div>

Custom items

To access any additional fields of the item use the origin property.

Example
x-model:

<div
  x-data="{ model: [], isLoading: false }"
  class="flex flex-col items-center"
>
  <div
    x-data="select"
    data-item-text="name"
    data-item-value="id"
    data-use-loader="true"
    :data-is-loading="isLoading"
    @open-selectmenu="() => {
      isLoading = true
      fetch('https://jsonplaceholder.typicode.com/users')
        .then(res => res.json())
        .then(json => {
          items = json
          isLoading = false
        })
      }"
    x-model="model"
    class="w-[340px]"
  >
    <div
      x-bind="trigger"
      x-data="input"
      class="flex flex-1 items-center rounded-sm border px-3 py-2 outline-hidden transition-shadow duration-200 focus-within:ring-3 focus:outline-hidden"
      class-default="border-gray-300 bg-white focus-within:border-gray-400 focus-within:ring-primary-200 dark:border-dark-600 dark:bg-dark-800 dark:text-text-300 dark:focus-within:ring-primary-300"
      class-valid="border-success-300 bg-white text-success-600 focus-within:ring-success-200 dark:border-success-400 dark:bg-dark-800 dark:text-success-600 dark:focus-within:ring-success-300"
      class-invalid="border-danger-300 bg-white text-danger-600 focus-within:ring-danger-200 dark:border-danger-400 dark:bg-dark-800 dark:text-danger-600 dark:focus-within:ring-danger-300"
    >
      <div data-icon class="mr-3 empty:hidden"></div>
      <div data-prepend class="mr-3 empty:hidden"></div>
      <div class="mr-3 flex flex-1 flex-wrap">
        <template x-for="selectedItem in getSelected()">
          <span
            x-text="selectedItem.text"
            class="mr-1 after:content-[','] last-of-type:mr-2 last-of-type:after:content-none"
          ></span>
        </template>
        <input
          x-bind="input"
          type="text"
          class="w-full min-w-0 flex-1 border-0 bg-transparent p-0 outline-hidden focus:min-w-0 focus:outline-hidden"
          readonly
        />
      </div>
      <div data-append class="mr-3 empty:hidden"></div>
      <div class="flex items-center gap-x-2">
        <div x-bind="loader">
          <svg
            viewBox="25 25 50 50"
            fill="none"
            class="h-5 w-5 animate-spinner-rotate"
          >
            <circle
              cx="50"
              cy="50"
              r="20"
              stroke="currentColor"
              stroke-width="4"
              stroke-miterlimit="10"
              stroke-linecap="round"
              class="animate-spinner-dash"
            />
          </svg>
        </div>
        <button
          x-bind="clearButton"
          @click="clearSelection()"
          class="flex items-center"
        >
          <svg
            xmlns="http://www.w3.org/2000/svg"
            width="16"
            height="16"
            fill="currentColor"
            class="pointer-events-none h-5 w-5 opacity-70"
            viewBox="0 0 16 16"
          >
            <path
              d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"
            />
          </svg>
        </button>
        <button>
          <svg
            xmlns="http://www.w3.org/2000/svg"
            fill="currentColor"
            viewBox="0 0 512 512"
            class="h-4 w-4 rotate-180 transform duration-200"
            :class="{ 'rotate-0!': isOpen }"
          >
            <path
              d="M233.4 105.4c12.5-12.5 32.8-12.5 45.3 0l192 192c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L256 173.3 86.6 342.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l192-192z"
            />
          </svg>
        </button>
      </div>
    </div>
    <div
      x-bind="menu"
      class="oveflow-y-auto z-50 max-h-[400px] overflow-x-hidden rounded-b-md bg-white shadow-md dark:bg-dark-800"
      x-alt-transition='{
        "enter": ["opacity-0 scale-y-[0.5]", "transition ease-out duration-100 origin-top", "opacity-100"],
        "leave": ["opacity-100", "transition ease-in duration-100 origin-top", "opacity-0 scale-y-[0.5]"]
      }'
    >
      <template x-if="getItems().length === 0">
        <div class="px-6 py-4 text-left text-text-800 dark:text-text-300">
          No items available.
        </div>
      </template>
      <template x-for="(item, index) in getItems()" :key="item.value">
        <button
          x-bind="option"
          class="w-full cursor-pointer scroll-mt-20 scroll-mb-20 px-6 py-4 text-left ring-secondary-400 transition-colors ring-inset focus:ring-2 focus:outline-hidden"
          class-default="text-text-800 hover:bg-secondary-100 focus:bg-secondary-200 dark:text-text-300 dark:hover:bg-dark-600 dark:focus:bg-dark-600"
          class-selected="bg-secondary-200 focus:bg-secondary-300 dark:bg-dark-600 dark:focus:bg-dark-700"
        >
          <div class="flex flex-col">
            <span x-text="item.text"></span>
            <span
              x-text="item.origin.company.name"
              class="text-sm text-text-500 dark:text-text-400"
            ></span>
            <span
              x-text="item.origin.address.city"
              class="text-xs text-text-500 dark:text-text-400"
            ></span>
          </div>
        </button>
      </template>
    </div>
  </div>
  <div class="mx-auto mt-4 w-max font-mono text-lg">
    x-model: <span x-text="JSON.stringify(model)"></span>
  </div>
</div>
Litewind-alpine 0.1.0