Skip to content

Select

Selects are interactive components that allow users to choose from a list of options.

Basic Usage

V-Model: ""
vue
<template>
  <spr-select
    id="select-basic"
    v-model="selectModel"
    label="Select Label"
    placeholder="Select an option"
    :options="options"
  />
</template>

<script lang="ts" setup>
import { ref } from 'vue';

const selectModel = ref('');

const options = ref([
  { text: 'Apple', value: 'apple' },
  { text: 'Banana', value: 'banana' },
  { text: 'Cherry', value: 'cherry' },
  { text: 'Date', value: 'date' },
  { text: 'Elderberry', value: 'elderberry' },
  { text: 'Fig', value: 'fig' },
  { text: 'Grape', value: 'grape' },
  { text: 'Nectarine', value: 'nectarine' },
  { text: 'Orange', value: 'orange' },
  { text: 'Papaya', value: 'papaya' },
  { text: '89 Quince', value: '50' },
]);
</script>

Grouped Items By

You can group items by default, A-Z or Z-A order by passing the group-items-by prop and specifying the desired grouping type.

vue
<template>
  <div class="spr-grid spr-gap-4">
    <spr-select
      id="select-grouped-items-by"
      v-model="selectModel"
      label="Select Label"
      placeholder="Select an option"
      :options="options"
      group-items-by="A-Z"
    />
  </div>
</template>

The search feature allows users to quickly filter and find specific items within the select list by typing in a search query.

  • Use the searchable-options prop to enable the search input within the select component.
V-Model: ""
vue
<template>
  <spr-select
    id="select-search"
    v-model="selectModel"
    label="Select Label"
    placeholder="Select an option"
    :options="options"
    searchable
  />
</template>

<script lang="ts" setup>
import { ref } from 'vue';

const selectModel = ref('');

const options = ref([
  { text: 'Apple', value: 'apple' },
  { text: 'Banana', value: 'banana' },
  { text: 'Cherry', value: 'cherry' },
  { text: 'Date', value: 'date' },
  { text: 'Elderberry', value: 'elderberry' },
  { text: 'Fig', value: 'fig' },
  { text: 'Grape', value: 'grape' },
  { text: 'Nectarine', value: 'nectarine' },
  { text: 'Orange', value: 'orange' },
  { text: 'Papaya', value: 'papaya' },
  { text: '89 Quince', value: '50' },
]);
</script>

You can disable local search by passing the disabled-local-search prop. This is useful when you want to handle search via API only, and not filter the options locally.

Use @searchString emit to get the search string when the user types in the search input. This allows you to handle the search logic externally, such as fetching options from an API based on the search query.

V-Model: ""
vue
<template>
  <spr-select
    id="select-search-disabled-local-search"
    v-model="selectModel"
    label="Select Label"
    placeholder="Select an option"
    :options="options"
    searchable
    disabled-local-search
  />
</template>

<script lang="ts" setup>
import { ref } from 'vue';

const selectModel = ref('');

const options = ref([
  { text: 'Apple', value: 'apple' },
  { text: 'Banana', value: 'banana' },
  { text: 'Cherry', value: 'cherry' },
  { text: 'Date', value: 'date' },
  { text: 'Elderberry', value: 'elderberry' },
  { text: 'Fig', value: 'fig' },
  { text: 'Grape', value: 'grape' },
  { text: 'Nectarine', value: 'nectarine' },
  { text: 'Orange', value: 'orange' },
  { text: 'Papaya', value: 'papaya' },
  { text: '89 Quince', value: '50' },
]);
</script>

Pre-Selected Items

Pre-selected items are options that are automatically selected when the select is first displayed. The v-model for the select component supports:

  • String: For single selection by value (e.g., 'apple').
  • Number: For single selection by number value (e.g., 42).
  • Array of strings or numbers: For single selection by array (e.g., ['apple'] or [42]).
  • Object: For single selection by object reference (e.g., { text: 'Apple', value: 'apple' }). See more in the Supported Value Types.
V-Model: apple
vue
<template>
  <div class="spr-grid spr-gap-4">
    <spr-select
      id="select-pre-selected-items"
      v-model="selectModel"
      label="Select Label"
      placeholder="Select an option"
      :options="options"
      group-items-by="A-Z"
    />
  </div>
</template>

<script lang="ts" setup>
import { ref, onMounted } from 'vue';

const selectModel = ref(['apple']);
</script>

You can also pre-select items with search functionality. This allows users to see the pre-selected items while still being able to search through the options.

Placements

Placement refers to where the select popper will be positioned relative to its trigger element (e.g., button, input field). Pass the placement props to modify the placement of the select popper.

The available placement options are: auto, auto-start, auto-end, top, top-start, top-end, right, right-start, right-end, bottom, bottom-start, bottom-end, left, left-start, and left-end.

The default placement is bottom.

Clearable

The clearable feature allows users to easily remove the selected value from the select input. This is particularly useful for forms where users may want to reset their selection without having to open the select.

V-Model: ""

Width and Popper Width

You can modify the width of the select component in two ways: by adjusting the width of the select wrapper or by changing the width of the select popper.

Width - Is the overall width wrapper of both parent element and popper element.

Popper Width - Width of only popper element

vue
<template>
  <spr-select
    id="select-width"
    v-model="selectModel"
    label="Select Label"
    placeholder="Select an option"
    :options="options"
    width="50%"
    popper-width="200px"
  />
</template>

Popper Strategy

The Popper strategy is primarily used when working with elements like modal. It helps control the positioning behavior of popper. The strategy ensures that the popper element is dynamically positioned based on the viewport, the reference element, or other factors such as scrolling or resizing.

By default, the Popper strategy is set to absolute, which positions the popper element relative to the nearest positioned ancestor (usually the body element). However, you can change the strategy to fixed, which positions the popper element relative to the viewport, ensuring that it remains in place even when the page is scrolled.

Pass the prop popper-strategy to change the behavior position of the popper.

Important to note:

Do not forget to pass prop wrapperPosition to overwrite relative position into initial.

vue
<template>
  <spr-button tone="success" @click="modalModel = true">Open Modal</spr-button>

  <spr-modal v-model="modalModel" title="Select with Modal">
    <spr-select
      id="select-popper-strategy"
      v-model="selectModel"
      label="Select Label"
      placeholder="Select an option"
      :options="options"
      popper-strategy="fixed"
      wrapper-position="initial"
    />
    <p>
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore
      magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
      consequat.
    </p>
  </spr-modal>
</template>

You can also use the popper-container prop to specify a custom container for the popper element. This is useful when you want to restrict the popper's positioning context to a specific element within your application.

Since the popper is being teleported to a different container, the popper-width prop will not work as expected. To set a custom width for the popper in this case, you can use custom styles or CSS classes to define the desired width.

vue
<template>
  <spr-dropdown
    id="dropdown-custom-popper"
    width="300px"
    :triggers="['hover', 'click']"
    :popper-triggers="['hover', 'click']"
    popper-width="500px"
    :auto-hide="false"
  >
    <spr-button class="spr-w-full" tone="success" has-icon>
      <span>Custom Popper With Dropdown</span>
      <Icon icon="ph:caret-down" />
    </spr-button>
    <template #popper>
      <spr-select
        id="select-dropdown-custom-popper"
        v-model="selectModel"
        label="Select Label"
        placeholder="Select an option"
        :options="options"
        popper-strategy="fixed"
        popper-container="#dropdown-custom-popper"
        wrapper-position="relative"
        placement="bottom"
      />
    </template>
  </spr-dropdown>
</template>

Infinite Scroll

Infinite scroll allows the select list to load more items as the user scrolls. This feature is particularly useful for back-end API integration. Instead of loading the entire list at once, new items are dynamically added as needed, improving performance and usability. Pass @infinite-scroll-trigger emit to get the trigger of options when it reaches bottom.

When working with infinite scroll and API-driven selects, you can use the display-text prop to show a display value in the input on initial load (for example, when you only have the selected value and not the full option object yet). This is especially helpful for large datasets where you don't want to fetch all options at once.

Paginated Options - Should load 10 Items per page:

Pagination:

{
  "totalpages": 10,
  "currentPage": 1
}

Data:

[]
vue
<template>
  <spr-select
    id="select-infinite-scroll"
    v-model="selectModel"
    label="Select Label"
    placeholder="Select an option"
    :options="optionsAPI"
    :display-text="displayText"
    @infinite-scroll-trigger="handleInfiniteScrollTrigger"
  />
</template>

<script lang="ts" setup>
import { ref } from 'vue';

const selectModel = ref(51); // Initial value for the select
const displayText = ref('Border Terrier'); // Display text for the selected option

const optionsAPI = ref<optionsType[]>([]);

const APIisLoading = ref(false);

const pagination = ref({
  totalpages: 10,
  currentPage: 1,
});

const setOptionsViaAPI = () => {
  getNextOptionsViaAPI();
};

const handleInfiniteScrollTrigger = () => {
  if (pagination.value.currentPage === pagination.value.totalpages || APIisLoading.value) return;

  APIisLoading.value = true;
  pagination.value.currentPage += 1;

  getNextOptionsViaAPI();
};

const getNextOptionsViaAPI = async () => {
  try {
    const response = await fetch(`https://api.thedogapi.com/v1/breeds?page=${pagination.value.currentPage}&limit=10`);

    if (!response.ok) {
      throw new Error('Network response was not ok');
    }

    const options = await response.json();

    optionsAPI.value = options.length
      ? [
          ...(optionsAPI.value || []),
          ...options.map((option) => ({
            text: option.name,
            value: option.id,
          })),
        ]
      : [];

    APIisLoading.value = false;
  } catch (error) {
    console.error('There was a problem with the fetch operation:', error);
  }
};
</script>

Active, Disabled, Error States

For guidance on implementing error, active, and disabled states in the select component, you can refer to the documentation for the input text component, as the approach is similar. See the Input Form for detailed instructions.

Active State

vue
<template>
  <spr-select
    id="select-active-state"
    v-model="selectModel"
    label="Select Label"
    placeholder="Select an option"
    :options="options"
    active
  />
</template>

Disabled State

vue
<template>
  <spr-select
    id="select-disabled-state"
    v-model="selectModel"
    label="Select Label"
    placeholder="Select an option"
    :options="options"
    disabled
  />
</template>

Error State

vue
<template>
  <spr-select
    id="select-error-state"
    v-model="selectModel"
    label="Select Label"
    placeholder="Select an option"
    :options="options"
    error
  />
</template>

Helper Message

A helper message is a text label below the input field that provides additional information about instructions, formatting hints, validation feedback, etc.

To display the helper message, set the display-helper prop to true and add the helper-text prop with the helper message text. You can also insert an icon with the helper-icon prop. This uses the Iconify icon library.

This is a helper message
This is an error message
vue
<template>
  <spr-select
    id="select-helper-message"
    v-model="selectModel"
    label="Select Label"
    placeholder="Select an option"
    :options="options"
    helper-text="This is a helper message"
    display-helper
  />

  <spr-select
    id="select-helper-message-error"
    v-model="selectModel"
    label="Select Label"
    placeholder="Select an option"
    :options="options"
    helper-text="This is an error message"
    helper-icon="ph:warning-circle-fill"
    display-helper
    error
  />
</template>

Alternatively, you can use the helperMessage slot to display a custom helper message.

This is a helper message
This is an error message
vue
<template>
  <spr-select
    id="select-helper-message-slot"
    v-model="selectModel"
    label="Select Label"
    placeholder="Select an option"
    :options="options"
    display-helper
  >
    <template #helperMessage>This is a helper message</template>
  </spr-select>

  <spr-select
    id="select-helper-message-error-slot"
    v-model="selectModel"
    label="Select Label"
    placeholder="Select an option"
    :options="options"
    display-helper
    error
  >
    <template #helperMessage>
      <icon icon="ph:warning-circle-fill" width="20px" height="20px" />
      <span>This is an error message</span>
    </template>
  </spr-select>
</template>

Supported Value Types

The select component supports various types of values. The v-model binding can accept different data formats depending on your needs.

Single Primitive Values

For single selection of primitive types like strings or numbers:

Value: apple

Value: 42

vue
<template>
  <spr-select
    id="select-string"
    v-model="stringValue"
    label="Select Label"
    placeholder="Select an option"
    :options="stringoptions"
  />

  <spr-select
    id="select-number"
    v-model="numberValue"
    label="Select Label"
    placeholder="Select an option"
    :options="numberoptions"
  />
</template>

<script lang="ts" setup>
import { ref } from 'vue';

// For string values
const stringValue = ref('apple'); // Single string value

// For number values
const numberValue = ref(42); // Single number value

const stringoptions = ref([
  { text: 'Apple', value: 'apple' },
  { text: 'Banana', value: 'banana' },
  { text: 'Cherry', value: 'cherry' },
]);

const numberoptions = ref([
  { text: '42', value: 42 },
  { text: '100', value: 100 },
  { text: '200', value: 200 },
]);
</script>

Single Object Values

For single selection of full objects:

Value: { "id": 1, "name": "John", "role": "Developer" }

vue
<template>
  <spr-select
    id="select-object"
    v-model="selectedUser"
    label="Select Label"
    placeholder="Select an option"
    :options="userList"
    text-field="name"      // Specify which field to display as text
    value-field="id"       // Specify which field to use as value
  />
</template>

<script lang="ts" setup>
import { ref } from 'vue';

// Object selection using full object reference
const selectedUser = ref({ id: 1, name: 'John', role: 'Developer' });

const userList = ref([
  { id: 1, name: 'John', role: 'Developer' },
  { id: 2, name: 'Jane', role: 'Designer' },
  { id: 3, name: 'Bob', role: 'Manager' }
]);
</script>

Item Subtext

The item subtext feature allows you to display additional information below the main text of each option in the select list. This can be useful for providing context or details about each option. Use the subtext props in the options array to specify the subtext for each option.

vue
<template>
  <spr-select
    id="select-item-subtext"
    v-model="selectModel"
    label="Select Label"
    placeholder="Select an option"
    :options="optionsWithSubtext"
  />
</template>

<script lang="ts" setup>
import { ref } from 'vue';

const selectModel = ref('');

const optionsWithSubtext = ref([
  { text: 'Apple', value: 'apple', subtext: 'A sweet red fruit' },
  { text: 'Banana', value: 'banana', subtext: 'A long yellow fruit' },
  { text: 'Cherry', value: 'cherry', subtext: 'A small red fruit' },
  { text: 'Date', value: 'date', subtext: 'A sweet brown fruit' },
  { text: 'Elderberry', value: 'elderberry', subtext: 'A small dark purple fruit' },
]);
</script>

Item Icon

The item icon feature allows you to display an icon alongside the text of each option in the select list. This can help users quickly identify options based on visual cues. Use the icon props in the options array to specify the icon for each option.

Icon uses phosphor icons from Iconify

vue
<template>
  <spr-select
    id="select-item-icon"
    v-model="selectModel"
    label="Select Label"
    placeholder="Select an option"
    :options="options"
    item-icon="ph:alarm"
  />
</template>

<script lang="ts" setup>
import { ref } from 'vue';

const selectModel = ref('');

const options = ref([
  { text: 'Apple', value: 'apple' },
  { text: 'Banana', value: 'banana' },
  { text: 'Cherry', value: 'cherry' },
  { text: 'Date', value: 'date' },
  { text: 'Elderberry', value: 'elderberry' },
]);
</script>

Item Custom Icon

The item custom icon feature allows you to display different icons for each option in the select list. This is useful for providing unique visual representations for each option. Add property icon in the options array to specify the custom icon for each option.

vue
<template>
  <spr-select
    id="select-item-custom-icon"
    v-model="selectModel"
    label="Select Label"
    placeholder="Select an option"
    :options="optionsWithCustomIcon"
  />
</template>

<script lang="ts" setup>
import { ref } from 'vue';

const selectModel = ref('');

const optionsWithCustomIcon = ref([
  { text: 'Acorn', value: 'acorn', icon: 'ph:acorn' },
  { text: 'Address Book', value: 'address book', icon: 'ph:address-book' },
  { text: 'Alarm', value: 'alarm', icon: 'ph:alarm' },
  { text: 'Angle', value: 'angle', icon: 'ph:angle' },
  { text: 'Apple Logo', value: 'apple logo', icon: 'ph:apple-logo' },
]);
</script>

Item Lozenge

The item lozenge feature allows you to display a colored lozenge (badge) alongside the text of each option in the select list. This can be useful for categorizing or highlighting options.

Lozenge Properties Reference:

  • label: The text label displayed inside the lozenge.
  • tone: The color tone of the lozenge. Available tones are: 'plain', 'pending', 'information', 'success', 'danger', 'neutral', 'caution'.
  • fill: A boolean indicating whether the lozenge should have a filled background (true) or an outlined style (false).
  • removevalue: A boolean indicating whether to show a remove icon inside the lozenge (true) or not (false).
  • url: The URL of the avatar image to be displayed inside the lozenge.
  • visible: A boolean indicating whether the lozenge is visible (true) or hidden (false).
  • loading: A boolean indicating whether to show a loading spinner inside the lozenge (true) or not (false).
  • icon: The icon to be displayed inside the lozenge.
  • postfixIcon: The icon to be displayed at the end of the lozenge.
  • interactive: A boolean indicating whether the lozenge is interactive (true) or not (false).
  • dropdown: A boolean indicating whether to show a dropdown arrow inside the lozenge (true) or not (false).

Display Items as Lozenge

Add property lozenge to display the selected item as a lozenge in the select input. Then if you want to change the lozenge style for each option, you can use lozengeProps to customize the lozenge appearance.

vue
<template>
  <spr-select
    id="select-display-item-lozenge"
    v-model="selectModel"
    label="Select Label"
    placeholder="Select an option"
    :options="options"
    lozenge
  />
</template>

<script lang="ts" setup>
import { ref } from 'vue';

const selectModel = ref('');

const options = ref([
  { text: 'Apple', value: 'apple' },
  { text: 'Carrot', value: 'carrot' },
  { text: 'Bread', value: 'bread' },
  { text: 'Chicken', value: 'chicken' },
  { text: 'Milk', value: 'milk' },
]);
</script>

Append Lozenge

Add property lozenge in the options array to specify the lozenge for each option.

vue
<template>
  <spr-select
    id="select-item-lozenge"
    v-model="selectModel"
    label="Select Label"
    placeholder="Select an option"
    :options="optionsWithLozenge"
  />
</template>

<script lang="ts" setup>
import { ref } from 'vue';

const selectModel = ref('');

const optionsWithLozenge = ref([
  {
    text: 'Apple',
    value: 'apple',
    lozenge: { label: 'Fruit', tone: 'plain' },
  },
  {
    text: 'Carrot',
    value: 'carrot',
    lozenge: { label: 'Vegetable', tone: 'pending' },
  },
  {
    text: 'Bread',
    value: 'bread',
    lozenge: { label: 'Grain', tone: 'information' },
  },
  {
    text: 'Chicken',
    value: 'chicken',
    lozenge: { label: 'Protein', tone: 'success' },
  },
  {
    text: 'Milk',
    value: 'milk',
    lozenge: { label: 'Dairy', tone: 'danger' },
  },
  {
    text: 'Eggs',
    value: 'eggs',
    lozenge: { label: 'Poultry', tone: 'neutral' },
  },
  {
    text: 'Fish',
    value: 'fish',
    lozenge: { label: 'Seafood', tone: 'caution' },
  },
]);
</script>

API Reference

NameDescriptionTypeDefault
idRequired to bind popper within the selectString-
v-modelValue binding for the select. Accepts:
  • Single primitive values: String ('apple'), Number (42)
  • Single object values: Full objects ({ id: 1, name: 'John' })
  • Array of values: Array of strings, numbers, or objects
The returned value type will match the input type (preserving strings, numbers, and objects).
String | Number | Object | Array[]
optionsList of options composed of text and value properties, or array of strings/objectsoptionsType[] | string[] | object[][]
group-items-byGroup items by order: 'A-Z', 'Z-A''A-Z' | 'Z-A'-
text-fieldField name to use for display text when using dynamic object arraysString'text'
value-fieldField name to use for value when using dynamic object arraysString'value'
display-textDisplay text to show in the input on initial load (useful for API-driven selects). This value is only shown once, and will be replaced when the user selects a new item.String-
placeholderPlaceholder text for the inputString-
labelLabel for the select input.String''
supporting-labelText beside label that has a supporting stylestring''
input-loaderDisplays a loading spinner inside the select input. Useful while asynchronously fetching options (e.g., API search). When true, the current value or placeholder is still shown with a spinner indicator.Booleanfalse
placementPlacement of the select popper (e.g., 'bottom', 'top', 'left', 'right')String'bottom'
searchable Searchable is to allow typing in the input. If searchable is not set or is false, the input will be readonly and users cannot type. Booleanfalse
disabled-local-search Disables local search when the searchable prop is set to true. This is useful when you want to handle search via API only. Booleanfalse
triggersArray of events that will trigger the dropdown to open'click' | 'hover' | 'focus' | 'touch'[]['click']
popper-triggersArray of events that will trigger the dropdown menu (popper element) to open'click' | 'hover' | 'focus' | 'touch'[][]
popper-strategyDefines how the select's popper is positioned: 'absolute' or 'fixed'String'absolute'
popper-widthWidth of the select's popperString'100%'
popper-containerCSS selector or HTMLElement to specify a custom container for the popper elementString | HTMLElement''
auto-hideWhen true, automatically hides the dropdown when clicking outside itBooleantrue
widthWidth of the select component wrapperString'100%'
wrapper-positionCSS position of the select wrapperString'relative'
disabledDisables the select if trueBooleanfalse
clearableAllows the user to clear the selected value with a clear buttonBooleanfalse
options-loaderDisplays a skeletal loading inside the popper. Useful while asynchronously fetching options (e.g., API search).Booleanfalse
infinite-scroll-loaderDisplays a loading spinner at the bottom of the list when infinite scroll is triggered. Useful when loading more items as the user scrolls to the end of the list.Booleanfalse
lozengeEnables lozenge mode for the list items. When enabled, items are displayed as lozenges.Booleanfalse
item-iconAny icon from iconify (e.g. ph:trash)String''

Events

EventPayloadDescription
@update:model-valueAnyEvent emitted when the model value changes
@infinite-scroll-triggerNoneEvent emitted when the select is scrolled to the bottom (for dynamic data loading)
@search-stringNoneEvent emitted when you type in the search input
@get-selected-optionObjectEvent emitted to get the selected option
@popper-stateBoleanEvent emitted when you open or close the popper

Exposed Methods

MethodDescriptionParameters
handleClearClears the current selection.()

Product Uses

Sprout HR
Sprout Sidekick