Skip to content

VPick

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

Usage

Options

Accepts the same options shape as VPickNative. Flat arrays and grouped arrays both work.

Examples

Grouped

Separators

Use separators to render a horizontal divider between adjacent groups.

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).

Disabled

Loading

Error

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.

Searchable

Use searchable to render an input trigger with built-in type-ahead filtering. The dropdown shows all options when opened, and filters as the user types.

Clearable

Use clearable to show a clear button when a value is selected. Works in both button and searchable modes.

Multiple

Use multiple to allow selecting more than one option. The trigger renders selected values as removable chips, and v-model becomes an array. Picking an option does not close the dropdown, so the user can keep selecting; pressing Backspace while the input is empty removes the last chip.

multiple always uses the searchable trigger so chips and the input share one row. Combine with clearable to expose a single button that empties the array.

Tree select

Pass options with a children array to enable tree mode. VPick detects nested data automatically — no extra prop needed. Click the chevron to expand or collapse a branch; clicking the row itself selects the node.

Use defaultExpandLevel to pre-expand branches on open. A value of 1 expands top-level branches; 2 expands two levels deep, and so on.

vue
<VPick :options="options" :default-expand-level="1" />

Use disableBranchNodes to make branch nodes non-selectable — only leaves can be picked.

vue
<VPick :options="options" disable-branch-nodes />

Combine with searchable to filter the tree. Matching nodes auto-expand their ancestor branches so results are always visible, and expansion reverts when the query is cleared.

Tree + cascade

Combine multiple with tree options to get cascade selection: clicking a branch checks all its descendants, clicking again unchecks them. An indeterminate dash appears when only some children are selected.

Use cascade: false to opt out and get independent node selection instead.

valueConsistsOf controls what ends up in v-model:

ValueWhat is emitted
"LEAF_PRIORITY" (default)Only leaf values. A fully-selected branch is implied by its leaves — no branch value appears in the array.
"BRANCH_PRIORITY"The topmost selected ancestor replaces its descendants. Selecting all of Electronics emits ["electronics"] rather than every leaf.
"ALL"Every checked node — both fully-selected branches and their leaf descendants.
"ALL_WITH_INDETERMINATE"Like ALL but also includes partially-selected (indeterminate) branch values.
vue
<VPick
  v-model="selected"
  :options="options"
  multiple
  value-consists-of="BRANCH_PRIORITY"
/>

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.

Props

These props apply to both VPickNative and VPick:

PropTypeDefaultDescription
modelValueanyundefinedSelected value. Use v-model for two-way binding. With multiple, an array of values.
optionsOptionOrGroup[]requiredArray of options or option groups.
placeholderstringundefinedPlaceholder text shown when no value is selected.
disabledbooleanfalseDisables the select.
loadingbooleanfalseShows a spinner and disables interaction.
errorstringundefinedError message. Applies error styling and aria-invalid.
size"default" | "sm""default"Size variant.
idstringundefinedHTML id attribute.
namestringundefinedHTML name attribute for form submission.
requiredbooleanfalseHTML required attribute.
ariaLabelstringundefinedaria-label for accessibility.
ariaDescribedbystringundefinedaria-describedby for accessibility.
labelKeystring"label"Object key to read each option's visible label from.
valueKeystring"value"Object key to read each option's value from.
disabledKeystring"disabled"Object key to read each option's disabled flag from.
groupOptionsKeystring"options"Object key for the options array inside a group.

VPick-only props

PropTypeDefaultDescription
separatorsbooleanfalseRenders a horizontal divider between adjacent groups in the dropdown.
rotateIconbooleanfalseRotates the trigger chevron 180 degrees when the dropdown is open.
searchablebooleanfalseRenders an input trigger with type-ahead filtering instead of a button.
clearablebooleanfalseShows a clear button when a value is selected.
multiplebooleanfalseAllows selecting multiple values. v-model becomes an array; selected values render as chips in the trigger.
filter(option, query) => booleanundefinedCustom filter function for searchable mode. Receives each option and the query string.
noResultsTextstring"No results"Text displayed when the search query matches no options.
teleportTostring | HTMLElement"body"CSS selector or element to mount the dropdown into. The dropdown escapes overflow: hidden ancestors.
bodyLockboolean | nullnullLocks body scroll while open. Defaults to true for button mode, false for searchable mode when set to null.
childrenKeystring"children"Object key for nested children. Options with a non-empty children array enable tree mode automatically.
defaultExpandLevelnumberundefinedNumber of levels to pre-expand on open. 1 expands top-level branches, 2 expands two levels, and so on.
disableBranchNodesbooleanfalseMakes branch nodes (those with children) non-selectable. Only leaf nodes can be picked.
cascadebooleantrueIn multiple tree mode, selecting a branch selects all its descendants. Set to false for independent node selection.
valueConsistsOf"LEAF_PRIORITY" | "ALL" | "BRANCH_PRIORITY" | "ALL_WITH_INDETERMINATE""LEAF_PRIORITY"Controls which nodes appear in v-model when cascade is active. See tree cascade section for details.

Slots

SlotScopeDescription
iconCustom chevron icon. Shown when not loading.
loadingCustom loading indicator. Shown when loading is true.
clearCustom clear button content. Shown when clearable and a value is selected.
empty{ query: string }Custom empty state when no options match the search query.

Events

EventPayloadDescription
searchstringEmitted on every keystroke in searchable mode.

Keyboard navigation

KeyAction
Enter / SpaceOpen dropdown / select focused option. In searchable mode, Space types normally.
EscapeClose dropdown. When closed and clearable, clears the selection.
Arrow Up / Arrow DownMove focus between options
HomeFocus first option
EndFocus last option
Arrow RightIn tree mode, expand a collapsed branch and move to its first child.
Arrow LeftIn tree mode, collapse an expanded branch; on a leaf or collapsed branch, jump to its parent.
BackspaceIn multiple mode, removes the last selected chip when the search input is empty.
TabClose dropdown and move focus

Accessibility

  • WAI-ARIA listbox pattern (role="combobox", role="listbox", role="option").
  • aria-expanded reflects open state on the trigger button.
  • aria-activedescendant tracks the focused option.
  • aria-multiselectable is set on the listbox in multiple mode, with aria-selected reflected per option.
  • aria-invalid is set when the error prop is present.
  • aria-disabled on individual disabled options.
  • A visually hidden native <select> is kept in sync for form submission and Safari autofill. In multiple mode it renders as <select multiple> and serializes the selected values.

Released under the MIT License.