vue-superselect

February 25, 2026 ยท View on GitHub

Headless, accessible, TypeScript-first select/combobox for Vue 3.

Zero runtime CSS. Full keyboard navigation. WAI-ARIA combobox pattern. Dual API: compound components or useSelect() composable.

Documentation

Install

npm install vue-superselect

Vue 3.5+ required. @floating-ui/vue is an optional peer dependency for smart dropdown positioning.

Quick Start

<script setup lang="ts">
import { ref } from 'vue'
import {
  SelectRoot,
  SelectControl,
  SelectInput,
  SelectContent,
  SelectOption,
} from 'vue-superselect'

const selected = ref<string | null>(null)
const fruits = ['Apple', 'Banana', 'Cherry', 'Grape', 'Orange']
</script>

<template>
  <SelectRoot v-model="selected">
    <SelectControl>
      <SelectInput placeholder="Pick a fruit..." aria-label="Fruit" />
    </SelectControl>
    <SelectContent>
      <SelectOption
        v-for="fruit in fruits"
        :key="fruit"
        :value="fruit"
        :label="fruit"
      >
        {{ fruit }}
      </SelectOption>
    </SelectContent>
  </SelectRoot>
</template>

The library ships zero CSS. Add your own classes and styles.

Features

  • Headless: no shipped CSS, full control over rendering via scoped slots
  • Accessible: WAI-ARIA combobox pattern, keyboard navigation, screen reader announcements
  • TypeScript: strict mode, full generic inference, typed props and slots
  • Single and multi-select: v-model with multiple, tags, max limits, hideSelected
  • Filtering: built-in case-insensitive filter, custom filter functions, debounce, IME-safe
  • Positioning: optional @floating-ui/vue integration with auto-flip, teleport support
  • Composable API: useSelect<T>() with prop getters for full DOM control
  • Tree-shakeable: sideEffects: false, ESM + CJS builds, /*#__PURE__*/ annotations

Components

ComponentPurpose
SelectRootState container. Manages selection, filtering, keyboard, and ARIA.
SelectControlWrapper around the input/tags area.
SelectInputSearch/filter input field.
SelectContentDropdown container with positioning.
SelectOptionIndividual selectable option with scoped slot props.
SelectTagRemovable tag for multi-select.
SelectTriggerToggle button for the dropdown.
SelectClearButton to clear the current selection.
SelectEmptyShown when no options match the filter.
SelectLiveRegionScreen reader announcements.

Composable API

For full control over the rendered DOM:

import { useSelect } from 'vue-superselect'

const {
  getRootProps,
  getInputProps,
  getListboxProps,
  getOptionProps,
  isOpen,
  value,
  visibleItems,
} = useSelect({
  items: options,
  labelKey: 'label',
  valueKey: 'id',
})

Spread prop getters on your own elements:

<div v-bind="getRootProps()">
  <input v-bind="getInputProps()" />
  <ul v-bind="getListboxProps()">
    <li v-for="item in visibleItems" :key="item.id" v-bind="getOptionProps(item)">
      {{ item.label }}
    </li>
  </ul>
</div>

Keyboard Navigation

KeyAction
Arrow Down/UpNavigate options
EnterSelect highlighted option
EscapeClose dropdown; pressing again clears input query
TabMove focus (or select highlighted option when selectOnTab is enabled)
BackspaceRemove last tag (multi-select, empty input)
Home/EndJump to first/last option

Browser Support

Modern browsers (Chrome, Firefox, Safari, Edge). No IE11.

License

MIT