Skip to content

Dropdown

Dropdowns appears when the user interacts with a trigger element (such as a button or a link) and is usually used for navigation menus, form selections, actions, and filters.

Basic Usage

Dropdowns are versatile UI components that can be integrated with various elements, such as buttons, chips, inputs, and more, to provide users with a list of options or actions.

vue
<template>
  <spr-dropdown
    id="sample-dropdown"
    v-model="dropdownModel"
    :menu-list="menuList"
    @update:model-value="handleSelectedItem"
  >
    <spr-input v-model="inputTextModel" label="Dropdown Label" readonly placeholder="Select item..." />
  </spr-dropdown>
</template>

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

const dropdownModel = ref([]);
const inputTextModel = ref('');

const menuList = 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' },
]);

const handleSelectedItem = (selectedItem) => {
  inputTextModel.value[inputModel] = menuList.value.find((item) => item.value === selectedItem[0]).text;
};
</script>

Multi Select

This feature allows users to select multiple options from the dropdown list. It provides a more flexible way of choosing several items without closing the dropdown. Just pass the prop multi-select.

vue
<template>
  <spr-dropdown
    id="sample-dropdown"
    v-model="dropdownModel"
    :menu-list="menuList"
    multi-select
    @update:model-value="handleSelectedItem"
  >
    <spr-input v-model="inputTextModel" label="Dropdown Label" readonly placeholder="Select item..." />
  </spr-dropdown>
</template>

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

const dropdownModel = ref([]);
const inputTextModel = ref('');

const menuList = 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' },
]);

const handleSelectedItem = (selectedItem) => {
  const selectedTexts = selectedItem
    .map((item) => menuList.value.find((menuItem) => menuItem.value === item).text)
    .join(', ');
  inputTextModel.value[inputModel] = selectedTexts;
};
</script>

Multiple Number Values

The dropdown component fully supports arrays of number values in multi-select mode, preserving the numeric type throughout the selection process.

Multiple Number Values Demo

Selected values: None

Current selection type:

undefined

Current selection:

[]
vue
<template>
  <spr-dropdown
    id="number-multi-dropdown"
    v-model="selectedNumbers"
    :menu-list="numberOptions"
    multi-select
    @update:model-value="handleSelectedItems"
  >
    <spr-input
      v-model="displayText"
      label="Select Numbers"
      readonly
      placeholder="Select numbers..."
    />
  </spr-dropdown>
</template>

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

// Define number options with numeric values
const numberOptions = [
  { text: 'One', value: 1 },
  { text: 'Two', value: 2 },
  { text: 'Three', value: 3 },
  { text: 'Four', value: 4 },
  { text: 'Five', value: 5 }
];

// Initialize with preselected values
const selectedNumbers = ref([1, 3]); // Selected "One" and "Three"
const displayText = ref('One, Three');

// Handle selected items
const handleSelectedItems = (items) => {
  // Numbers are preserved in the items array
  const selectedTexts = items.map(value => {
    const option = numberOptions.find(opt => opt.value === value);
    return option ? option.text : '';
  }).filter(Boolean).join(', ');
  
  // Update display text
  displayText.value = selectedTexts;
};
</script>

Key Points About Number Value Support

  1. Value Type Preservation: Number values remain as numbers in the v-model
  2. Type-Safe Comparison: Both direct number comparison and string representation comparison are supported
  3. Pre-Selected Values: You can pre-select number values using an array of numbers
  4. Mixed Types: While supported, we recommend using consistent types (all strings or all numbers) for better maintainability

For more details and advanced examples, see Dropdown Number Multi-Select Example.

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. See List: Grouped Items for more examples.

vue
<template>
  <div class="spr-grid spr-gap-4">
    <spr-dropdown
      id="sample-dropdown1"
      v-model="dropdownModel.inputText3"
      :menu-list="menuList"
      group-items-by="A-Z"
      @update:model-value="handleSingleSelectedItem"
    >
      <spr-input v-model="inputTextModel" label="Single Select Dropdown" readonly placeholder="Select item..." />
    </spr-dropdown>

    <spr-dropdown
      id="sample-dropdown2"
      v-model="dropdownModel.inputText4"
      :menu-list="menuList"
      group-items-by="A-Z"
      multi-select
      @update:model-value="handleSelectedItem"
    >
      <spr-input v-model="inputTextModel" label="Multi Select Dropdown" readonly placeholder="Select item..." />
    </spr-dropdown>
  </div>
</template>

Pre-Selected Items

Pre-selected items are options that are automatically selected when the dropdown is first displayed. You can achieve this by adding the value as an array of strings to the v-model.

vue
<template>
  <div class="spr-grid spr-gap-4">
    <spr-dropdown
      id="dropdownSample1"
      v-model="dropdownModel.dropdownModel1"
      :menu-list="menuList"
      group-items-by="A-Z"
      @update:model-value="(value) => handleSelectedItem(value, 'single', 'inputText5')"
    >
      <spr-input
        v-model="inputTextModel.inputTextModel1"
        label="Single Select Dropdown"
        readonly
        placeholder="Select item..."
      />
    </spr-dropdown>
    <spr-dropdown
      id="dropdownSample26"
      v-model="dropdownModel.dropdownModel2"
      :menu-list="menuList"
      multi-select
      group-items-by="A-Z"
      @update:model-value="(value) => handleSelectedItem(value, 'multi', 'inputText6')"
    >
      <spr-input
        v-model="inputTextModel.inputTextModel2"
        label="Multi Select Dropdown"
        readonly
        placeholder="Select item..."
      />
    </spr-dropdown>
  </div>
</template>

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

const dropdownModel = ref({
  dropdownModel1: ['apple'],
  dropdownModel2: ['date', 'fig', 'orange'],
});

const inputTextModel = ref({
  inputTextModel1: '',
  inputTextModel2: '',
});

// Load selected values to input texts
onMounted(() => {
  handleSelectedItem(dropdownModel.value.dropdownModel1, 'single', 'inputTextModel1');
  handleSelectedItem(dropdownModel.value.dropdownModel2, 'multi', 'inputTextModel2');
});

const handleSelectedItem = (selectedItem, dropdownType, inputModel) => {
  if (dropdownType === 'single') {
    inputTextModel.value[inputModel] = menuList.value.find((item) => item.value === selectedItem[0]).text;
  }

  if (dropdownType === 'multi') {
    const selectedTexts = selectedItem
      .map((item) => menuList.value.find((menuItem) => menuItem.value === item).text)
      .join(', ');

    inputTextModel.value[inputModel] = selectedTexts;
  }
};
</script>

Placements

Placement refers to where the dropdown popper will be positioned relative to its trigger element (e.g., button, input field). Pass the placement props to modify the placement of the dropdown 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.

The search feature allows users to quickly filter and find specific items within the dropdown list by typing in a search query. Pass down the string text of the item with the prop search-string.

vue
<template>
  <spr-dropdown
    id="sample-dropdown"
    v-model="dropdownModel.sampleDropdown"
    :menu-list="menuList"
    :search-string="inputTextModel"
    @update:model-value="handleSelectedItem"
  >
    <spr-input v-model="inputTextModel" label="Dropdown Label" placeholder="Select item..." />
  </spr-dropdown>
</template>

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

const inputTextModel = ref('');
const dropdownModel = ref('');

const menuList = 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' },
]);

const handleSelectedItem = (selectedItem) => {
  inputTextModel.value[inputModel] = menuList.value.find((item) => item.value === selectedItem[0]).text;
};
</script>

Width and Popper Width

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

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

Popper Width - Width of only popper element

vue
<template>
  <spr-dropdown
    id="sample-dropdown"
    :menu-list="menuList"
    width="50%"
    popper-width="200px"
    @update:model-value="handleSelectedItem"
  >
    <spr-input v-model="inputTextModel" label="Dropdown Label" placeholder="Select item..." readonly />
  </spr-dropdown>
</template>

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

const inputTextModel = ref('');

const menuList = 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' },
]);

const handleSelectedItem = (selectedItem) => {
  inputTextModel.value = menuList.value.find((item) => item.value === selectedItem[0]).text;
};
</script>

Other Components Integration

Dropdowns can be integrated with various UI components like buttons, inputs, and chips. This allows for seamless interaction with other parts of the interface, ensuring consistency and enhancing user experience across the application.

Basic Chip
Plain
vue
<template>
  <div class="spr-grid spr-gap-4">
    <spr-dropdown id="sample-dropdown1" :menu-list="menuList" width="200px" popper-width="200px">
      <spr-button tone="success">Dropdown</spr-button>
    </spr-dropdown>

    <spr-dropdown id="sample-dropdown2" :menu-list="menuList" width="200px" popper-width="200px">
      <spr-chips label="Basic Chip" />
    </spr-dropdown>

    <spr-dropdown id="sample-dropdown3" :menu-list="menuList" width="200px" popper-width="200px">
      <spr-lozenge label="Plain" />
    </spr-dropdown>
  </div>
</template>

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

const menuList = 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>

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="Dropdown with Modal">
    <spr-dropdown
      id="sample-dropdown"
      v-model="dropdownModel"
      :menu-list="menuList"
      wrapper-position="initial"
      popper-stategy="fixed"
      @update:model-value="handleSelectedItem"
    >
      <spr-input v-model="inputTextModel" label="Dropdown Label" placeholder="Select item..." readonly />
    </spr-dropdown>
    <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>

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

const dropdownModel = ref([]);
const inputTextModel = ref('');
const modalModel = ref(false);

const menuList = 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' },
]);

const handleSelectedItem = (selectedItem) => {
  inputTextModel.value[inputModel] = menuList.value.find((item) => item.value === selectedItem[0]).text;
};
</script>

Infinite Scroll

Infinite scroll allows the dropdown list to load more items as the user scrolls, this feature is particularly 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 menu when it reaches bottom.

Paginated Menu List - Should load 10 Items per page:

Pagination:

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

Data:

[ { "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": "Honeydew", "value": "honeydew" }, { "text": "Indian Fig", "value": "indian_fig" }, { "text": "Jackfruit", "value": "jackfruit" } ]
vue
<template>
  <spr-dropdown id="sample-dropdown" :menu-list="menuList" @infinite-scroll-trigger="handleInfiniteScrollTrigger">
    <spr-input v-model="inputTextModel" label="Dropdown Label" placeholder="Select item..." readonly />
  </spr-dropdown>
</template>

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

const inputTextModel = ref('');
const modalModel = ref(false);

const menuList = 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' },
]);

const APIisLoading = ref(false);

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

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

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

  getGhibliFilms();
};

const getGhibliFilms = async () => {
  try {
    const response = await fetch('https://ghibliapi.vercel.app/films');

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

    const options = await response.json();

    paginatedMenuList.value = options.length
      ? [
          ...(paginatedMenuList.value || []),
          ...options.map((option: { id: string; original_title_romanised: string }) => ({
            text: option.original_title_romanised,
            value: option.id.replace(/\s+/g, ''),
          })),
        ]
      : [];

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

Ladderized Dropdown

Ladderized dropdown utilizes the Ladderized List component to display the dropdown items in a hierarchical manner. The v-model prop is an array of strings representing order of selected items per level.

vue
<template>
  <div>
    <spr-dropdown
      id="dropdown28"
      :menu-list="mockDropdownData"
      v-model="dropdownModel"
      :ladderized="true"
      @update:model-value="handleLadderizedDropdown"
    >
      <spr-input v-model="inputTextModel" label="Ladderized Dropdown" placeholder="Select item..." />
    </spr-dropdown>
  </div>
</template>

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

const dropdownModel = ref([]);
const inputTextModel = ref('');

const mockDropdownData = [
  {
    text: 'Lion',
    value: 'lion',
    subtext: 'King of the jungle',
    sublevel: [
      {
        text: 'Cub',
        value: 'cub',
        subtext: 'Young lion',
        sublevel: [
          {
            text: 'Cub 1',
            value: 'cub1',
          },
          {
            text: 'Cub 2',
            value: 'cub2',
          },
        ],
      },
      {
        text: 'Pride Member',
        value: 'pride-member',
        subtext: 'Member of a lion pride',
      },
    ],
  },
  {
    text: 'Elephant',
    value: 'elephant',
    subtext: 'Largest land animal',
    sublevel: [
      {
        text: 'Calf',
        value: 'calf',
        subtext: 'Young elephant',
      },
    ],
  },
  {
    text: 'Giraffe',
    value: 'giraffe',
    subtext: 'Tallest living terrestrial animal',
    sublevel: [
      {
        text: 'Calf',
        value: 'giraffe-calf',
        subtext: 'Young giraffe',
      },
      {
        text: 'Adult',
        value: 'giraffe-adult',
        subtext: 'Mature giraffe',
      },
    ],
  },
  {
    text: 'Zebra',
    value: 'zebra',
    subtext: 'Known for distinctive black and white stripes',
    sublevel: [
      {
        text: 'Foal',
        value: 'zebra-foal',
        subtext: 'Young zebra',
      },
      {
        text: 'Mare',
        value: 'zebra-mare',
        subtext: 'Adult female zebra',
      },
    ],
  },
];

const handleLadderizedDropdown = (value) => {
  let tempValue: string[] = [];
  let tempMenuList: MenuListType[] = mockDropdownData;

  value.forEach((item) => {
    const activeItem = tempMenuList.find((listItem) => item === listItem.value);

    if (activeItem) {
      tempValue.push(activeItem.text);
      if (activeItem.sublevel) {
        tempMenuList = activeItem.sublevel;
      }
    }
  });

  inputTextModel.value.inputText28 = tempValue.join(', ');
};
</script>

Ladderized dropdown search allows users to filter items in a hierarchical manner. This feature is particularly useful when dealing with large datasets, as it enables users to quickly locate specific items within nested structures. Can be used if the ladderized prop is set to true and searchString prop has a value.

LIMITATION

Ladderized dropdown search does not support multi-select and can only search through two hierarchical levels (root level and sublevel).

vue
<template>
  <div>
    <spr-dropdown
      id="dropdown28"
      :menu-list="mockDropdownData"
      v-model="dropdownModel"
      :ladderized="true"
      :search-string="inputTextModel"
      @update:model-value="handleLadderizedDropdown"
    >
      <spr-input v-model="inputTextModel" label="Ladderized Dropdown" placeholder="Select item..." />
    </spr-dropdown>
  </div>
</template>

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

const dropdownModel = ref([]);
const inputTextModel = ref('');

const mockDropdownData = [
  {
    text: 'Lion',
    value: 'lion',
    subtext: 'King of the jungle',
    sublevel: [
      {
        text: 'Cub',
        value: 'cub',
        subtext: 'Young lion',
        sublevel: [
          //cannot be searched
          {
            text: 'Cub 1', 
            value: 'cub1', 
          }, 
          {
            text: 'Cub 2', 
            value: 'cub2', 
          }, 
        ], 
      },
      {
        text: 'Pride Member',
        value: 'pride-member',
        subtext: 'Member of a lion pride',
      },
    ],
  },
  {
    text: 'Elephant',
    value: 'elephant',
    subtext: 'Largest land animal',
    sublevel: [
      {
        text: 'Calf',
        value: 'calf',
        subtext: 'Young elephant',
      },
    ],
  },
  {
    text: 'Giraffe',
    value: 'giraffe',
    subtext: 'Tallest living terrestrial animal',
    sublevel: [
      {
        text: 'Calf',
        value: 'giraffe-calf',
        subtext: 'Young giraffe',
      },
      {
        text: 'Adult',
        value: 'giraffe-adult',
        subtext: 'Mature giraffe',
      },
    ],
  },
  {
    text: 'Zebra',
    value: 'zebra',
    subtext: 'Known for distinctive black and white stripes',
    sublevel: [
      {
        text: 'Foal',
        value: 'zebra-foal',
        subtext: 'Young zebra',
      },
      {
        text: 'Mare',
        value: 'zebra-mare',
        subtext: 'Adult female zebra',
      },
    ],
  },
];

const handleLadderizedDropdown = (value) => {
  let tempValue: string[] = [];
  let tempMenuList: MenuListType[] = mockDropdownData;

  value.forEach((item) => {
    const activeItem = tempMenuList.find((listItem) => item === listItem.value);

    if (activeItem) {
      tempValue.push(activeItem.text);
      if (activeItem.sublevel) {
        tempMenuList = activeItem.sublevel;
      }
    }
  });

  inputTextModel.value = tempValue.join(', ');
};
</script>

Disabled, Active & Readonly

This is only applicable to selected components, such as form input fields. You can learn more in the Input Form.

To disable the popper from showing when the wrapper is clicked, pass the disabled prop.

Supported Value Types

The dropdown component supports various types of values for both single and multi-selection. 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-dropdown
    id="string-dropdown"
    v-model="stringValue"
    :menu-list="stringMenuList"
    @update:model-value="handleStringSelection"
  >
    <spr-input v-model="stringDisplay" label="String Selection" readonly placeholder="Select a fruit..." />
  </spr-dropdown>

  <spr-dropdown
    id="number-dropdown"
    v-model="numberValue"
    :menu-list="numberMenuList"
    @update:model-value="handleNumberSelection"
  >
    <spr-input v-model="numberDisplay" label="Number Selection" readonly placeholder="Select a number..." />
  </spr-dropdown>
</template>

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

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

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

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

const numberMenuList = ref([
  { text: '42', value: 42 },
  { text: '100', value: 100 },
  { text: '200', value: 200 }
]);

const handleStringSelection = () => {
  const selected = stringMenuList.value.find(item => item.value === stringValue.value);
  stringDisplay.value = selected ? selected.text : '';
};

const handleNumberSelection = () => {
  const selected = numberMenuList.value.find(item => item.value === numberValue.value);
  numberDisplay.value = selected ? selected.text : '';
};
</script>

Single Object Values

For single selection of full objects:

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

vue
<template>
  <spr-dropdown
    id="object-dropdown"
    v-model="selectedUser"
    :menu-list="userList"
    text-field="name"      <!-- Specify which field to display as text -->
    value-field="id"       <!-- Specify which field to use as value -->
    @update:model-value="handleUserSelection"
  >
    <spr-input v-model="userDisplay" label="User Selection" readonly placeholder="Select a user..." />
  </spr-dropdown>
</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 userDisplay = ref('John');

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

const handleUserSelection = () => {
  // When using full objects, the display value should be updated
  userDisplay.value = selectedUser.value.name;
};
</script>

Multiple Primitive Values

For multi-selection of primitive types like strings or numbers:

Value: [ "apple", "banana" ] (Fruits)

Value: [ 1, 2, 3 ] (Numbers)

vue
<template>
  <spr-dropdown
    id="multi-string-dropdown"
    v-model="selectedFruits"
    :menu-list="fruitList"
    multi-select
    @update:model-value="handleFruitsSelection"
  >
    <spr-input v-model="fruitsDisplay" label="Fruits Selection" readonly placeholder="Select fruits..." />
  </spr-dropdown>

  <spr-dropdown
    id="multi-number-dropdown"
    v-model="selectedNumbers"
    :menu-list="numbersList"
    multi-select
    @update:model-value="handleNumbersSelection"
  >
    <spr-input v-model="numbersDisplay" label="Numbers Selection" readonly placeholder="Select numbers..." />
  </spr-dropdown>
</template>

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

// Multiple string values
const selectedFruits = ref(['apple', 'banana']);  // Array of strings
const fruitsDisplay = ref('Apple, Banana');

// Multiple number values
const selectedNumbers = ref([1, 2, 3]);  // Array of numbers
const numbersDisplay = ref('1, 2, 3');

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

const numbersList = ref([
  { text: '1', value: 1 },
  { text: '2', value: 2 },
  { text: '3', value: 3 },
  { text: '4', value: 4 },
]);

const handleFruitsSelection = () => {
  const selectedTexts = selectedFruits.value.map(value => {
    const item = fruitList.value.find(fruit => fruit.value === value);
    return item ? item.text : '';
  });
  fruitsDisplay.value = selectedTexts.filter(Boolean).join(', ');
};

const handleNumbersSelection = () => {
  const selectedTexts = selectedNumbers.value.map(value => {
    const item = numbersList.value.find(num => num.value === value);
    return item ? item.text : '';
  });
  numbersDisplay.value = selectedTexts.filter(Boolean).join(', ');
};
</script>

Multiple Object Values

For multi-selection of full objects:

vue
<template>
  <spr-dropdown
    id="multi-object-dropdown"
    v-model="selectedUsers"
    :menu-list="usersList"
    text-field="name"
    value-field="id"
    multi-select
    @update:model-value="handleUsersSelection"
  >
    <spr-input v-model="usersDisplay" label="Team Members" readonly placeholder="Select team members..." />
  </spr-dropdown>
</template>

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

// Multiple object values
const selectedUsers = ref([
  { id: 1, name: 'John', role: 'Developer' },
  { id: 2, name: 'Jane', role: 'Designer' }
]);
const usersDisplay = ref('John, Jane');

const usersList = ref([
  { id: 1, name: 'John', role: 'Developer' },
  { id: 2, name: 'Jane', role: 'Designer' },
  { id: 3, name: 'Bob', role: 'Manager' },
  { id: 4, name: 'Alice', role: 'Product Owner' }
]);

const handleUsersSelection = () => {
  // When using full objects, extract the display names
  const names = selectedUsers.value.map(user => user.name);
  usersDisplay.value = names.join(', ');
};
</script>

API Reference

NameDescriptionTypeDefault
idRequired to bind popper within the datepickerArray-
v-modelValue binding for the dropdown. Accepts:
  • Single primitive values: String ('apple'), Number (42)
  • Single object values: Full objects ({ id: 1, name: 'John' })
  • Multiple primitive values: Array of strings (['apple', 'banana']), Array of numbers ([1, 2, 3])
  • Multiple object values: Array of objects ([{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }])
The returned value type will match the input type (preserving strings, numbers, and objects).
String | Number | Object | Array[]
menu-listList of options composed of `text` and `value` propertiesMenuListType[] (see List)[]
widthDefines the width of the dropdown component wrapperString`100%`
popper-widthDefines the width of the dropdown's popperString`100%`
popper-strategyDefines how the dropdown's popper is positioned relative to the reference element. Can be `absolute` or `fixed`string`absolute`
placementChanges the placement of the dropdown popper (e.g., `bottom`, `top`, `left`, `right`)string`bottom`
multi-selectIf set, allows multiple selections in the dropdownBooleanfalse
disabledIf set, allows to disabled popper on click to the wrapperBooleanfalse
search-stringSearch string to filter the dropdown listString-
ladderizedIf set, allows to display dropdown items in a hierarchical manner.Booleanfalse
@get-popper-stateEvent emitted when the popper state is changed (e.g., opened or closed)Function-
@infinite-scroll-triggerEvent emitted when the dropdown is scrolled to the bottom. Useful for dynamic data loadingFunction-
remove-current-level-in-back-labelDefines whether the back label in the ladderized list will exclude the current levelBooleanfalse

Product Uses

Sprout HR
Sprout Sidekick