Skip to content

VPick ​

A custom dropdown triggered by a button, with full keyboard navigation and group labels.

This page covers the parts every VPick has. Each of the bigger capabilities has its own page:

PageWhat it covers
SearchType-ahead filtering, clearing, empty states
MultiselectMultiple values, chips, select and deselect
Tree SelectNested options, cascade selection, searching a tree
SlotsReplacing labels, icons, and the empty state
PositioningWhere the panel renders, scroll containers, inline mode
FormsLabelling, submission, validation
AccessibilityKeyboard navigation, ARIA roles, screen readers
APIEvery prop, slot, event, and method in one place

Options ​

Accepts the same options shape as VPickNative. Flat arrays and grouped arrays both work, and groups are detected automatically when an item has an options array.

Grouped ​

Separators ​

Use separators to render a horizontal divider between adjacent groups.

Custom data shape ​

Use labelKey, valueKey, disabledKey, and groupOptionsKey to pass data straight from your API without mapping. See the Data Shape guide for the full reference.

labelKey also accepts an array, used as a fallback chain where the first key with a non-empty value wins. Useful when records are inconsistent:

vue
<VPick :options="options" :label-key="['label', 'name']" value-key="id" />

States ​

Disabled ​

Loading ​

Error ​

Appearance ​

Rotate icon ​

Rotates the chevron 180 degrees when the dropdown is open.

Scrollable ​

Long option lists scroll inside the dropdown. Max height is controlled by --vpick-listbox-max-height (default 16rem).

Sizing ​

By default, the trigger hugs its content (--vpick-width: fit-content) and the dropdown matches the trigger width at minimum. Give the trigger an explicit width and the dropdown will follow.

vue
<VPick v-model="selected" :options="options" style="--vpick-width: 18rem" />

Options wider than the trigger make the dropdown grow. To pin both widths identical and truncate long labels, also cap the listbox:

vue
<VPick
  v-model="selected"
  :options="options"
  style="
    --vpick-width: 18rem;
    --vpick-listbox-max-width: var(--vpick-trigger-width);
  "
/>
VariableDefaultEffect
--vpick-widthfit-contentTrigger width.
--vpick-listbox-min-width--vpick-trigger-widthMinimum dropdown width.
--vpick-listbox-max-widthcalc(100vw - 16px)Maximum dropdown width.
--vpick-listbox-max-height16remMaximum dropdown height before scrolling.

--vpick-trigger-width is measured and set by Vue Pick on every open, so read it rather than assign it.

Object values ​

By default v-model holds plain values:

js
selected = ["apple", "banana"]

Set valueFormat="object" and it holds your original option objects instead:

js
selected = [
  { id: "apple", label: "Apple", price: 3 },
  { id: "banana", label: "Banana", price: 2 },
]

Useful when you need other fields off the selection and would otherwise look them up yourself. Both controls below hold the same selection:

vue
<VPick
  v-model="selected"
  :options="fruit"
  label-key="label"
  value-key="id"
  value-format="object"
/>

Objects you pass back in are matched by valueKey, not by identity, so a rebuilt object literal still resolves:

js
selected = { id: "apple" } // matches the Apple option

The hidden <select> used for form submission always posts plain values, since a form field cannot carry an object.

Core props ​

The props every VPick takes. The rest are listed on the page for the feature they belong to, and all of them together on the API Reference.

modelValue

any default undefined

Selected value. Use v-model for two-way binding. In Vue 2 the prop is value.

options

OptionOrGroup[] required

Array of options or option groups.

Read more

placeholder

string default undefined

Text shown when no value is selected.

disabled

boolean default false

Disables the control.

loading

boolean default false

Shows a spinner and disables interaction.

error

string default undefined

Error message. Applies error styling and aria-invalid.

id

string default undefined

HTML id, applied to the control itself so a <label for> resolves to it.

Read more

name

string default undefined

HTML name for form submission.

Read more

required

boolean default false

HTML required attribute.

ariaLabel

string default undefined

aria-label, for when there is no visible label.

ariaDescribedby

string default undefined

aria-describedby, for wiring up hint or error text.

Data shape ​

labelKey

string | string[] default "label"

Key to read each option's visible label from. An array acts as a fallback chain: the first key with a non-empty value wins.

Read more

valueKey

string default "value"

Key to read each option's value from.

Read more

disabledKey

string default "disabled"

Key to read each option's disabled flag from.

Read more

groupOptionsKey

string default "options"

Key for the options array inside a group.

Read more

childrenKey

string default "children"

Key for nested children. Any option carrying that array turns tree mode on.

Read more

Appearance ​

separators

boolean default false

Draws a divider between adjacent groups in the list.

rotateIcon

boolean default false

Rotates the trigger chevron 180 degrees while open.

animate

boolean default true

Animates the multiselect chips. false adds and removes them outright.

Read more

optionAttrs

(option) => object default undefined

Extra attributes for each option row, such as test ids. Core attributes like role and aria-* cannot be overridden.

Read more

classes

object default {}

Extra classes for each part, such as trigger, chip or option. chip and option also take (option) => string.

Read more

Released under the MIT License.