Skip to content

Migrating from vue-treeselect ​

This page maps @riophae/vue-treeselect 0.4 to VPick. VPick runs on Vue 2.7 and Vue 3 with the same props, so you can replace the old component while still on 2.7 and keep it after you upgrade.

Use your AI agent ​

Paste this into your coding agent (Claude Code, Cursor, Copilot and so on) from the root of your project:

text
Replace @riophae/vue-treeselect with vue-pick in this project.

1. Read https://vue-pick.js.org/migration/vue-treeselect.md for the mapping
   and https://vue-pick.js.org/llms-full.txt for the full vue-pick API.
2. Install vue-pick. Import "vue-pick/style.css" once in the app entry.
3. Replace every <treeselect> with <VPick>. Import VPick from "vue-pick/vue2"
   on Vue 2.7 and from "vue-pick" on Vue 3.
4. Map props, events and slots using the tables in the guide. Add the props
   under "Keep the same behavior" so each component behaves as it does today.
5. Keep the shape of every bound value unchanged.
6. Where a prop has no equivalent, leave a TODO comment instead of guessing.
7. Remove @riophae/vue-treeselect and its CSS import once nothing uses it.
8. List every file you changed and every TODO you left.

If you wrap treeselect in your own component, ask the agent to change the wrapper first. Every page that uses it then moves over at once.

Install ​

sh
npm install vue-pick
npm uninstall @riophae/vue-treeselect
ts
// main.ts: replaces "@riophae/vue-treeselect/dist/vue-treeselect.css"
import "vue-pick/style.css"
ts
import { VPick } from "vue-pick/vue2" // Vue 2.7
import { VPick } from "vue-pick" // Vue 3

A basic swap ​

vue
<!-- Before -->
<treeselect v-model="value" :options="options" :multiple="true" />

<!-- After -->
<VPick
  v-model="value"
  :options="options"
  multiple
  searchable
  clearable
  value-key="id"
  disabled-key="isDisabled"
  value-consists-of="BRANCH_PRIORITY"
/>

vue-treeselect reads id, label, children and isDisabled from each option. VPick reads value, label, children and disabled, so the two keys that differ are set with value-key and disabled-key. Your options array stays as it is.

Keep the same behavior ​

These defaults differ. Set the VPick prop to keep what vue-treeselect did:

vue-treeselect defaultVPick defaultTo keep it
searchable: truefalsesearchable
clearable: truefalseclearable
valueConsistsOf: BRANCH_PRIORITYLEAF_PRIORITYvalue-consists-of="BRANCH_PRIORITY"
closeOnSelect: truestays open in multiple:close-on-select="true"
clearOnSelect: falsetrue:clear-on-select="false"
One chip per value entryMerges full branches:compact-chips="false"
Ticking an unloaded branch selects only its idLoads its children first:load-on-select="false"

The last two only matter in multiple tree mode, and the last one only with lazy loading.

Props ​

vue-treeselectVPick
value / v-modelv-model
optionsoptions
normalizerlabel-key, value-key, children-key, disabled-key (see below)
multiplemultiple
flat:cascade="false"
valueConsistsOfvalue-consists-of, same four values
valueFormatvalue-format, "id" or "object"
sortValueBysort-value-by, same three values
defaultExpandLeveldefault-expand-level
disableBranchNodesdisable-branch-nodes
searchablesearchable
searchNestedsearch-nested
flattenSearchResultsflatten-search-results
matchKeyssearch-keys (the label is always searched)
clearableclearable
clearOnSelectclear-on-select
closeOnSelectclose-on-select
alwaysOpenalways-open
backspaceRemovesbackspace-removes
deleteRemovesdelete-removes
loadOptions (LOAD_CHILDREN_OPTIONS)load-children
loadOptions (ASYNC_SEARCH) + asyncfetch-options
noChildrenTextno-children-text
noOptionsTextno-options-text
noResultsTextno-results-text
searchPromptTextsearch-prompt-text
loadingTextloading-children-text, searching-text
retryTextload-children-error-text, search-error-text
placeholder, disabled, name, requiredsame names
instanceIdid
appendToBodyNot needed. The list already renders outside clipping containers. See Positioning.
maxHeight--vpick-listbox-max-height CSS variable
zIndex--vpick-listbox-z-index CSS variable
openDirectionNot needed. The list flips when there is no room below.
autoFocusCall focus() on the component ref in onMounted
joinValues, delimiterNot needed. With name, each selected value posts under that name.

normalizer ​

VPick reads keys, not a function. When normalizer only renames fields, use the key props:

vue
<!-- Before -->
<treeselect
  :options="options"
  :normalizer="
    (node) => ({ id: node.key, label: node.name, children: node.subOptions })
  "
/>

<!-- After -->
<VPick
  :options="options"
  value-key="key"
  label-key="name"
  children-key="subOptions"
/>

If it computes a value, for example joining two fields into a label, map your options once with computed and pass the result. label-key also takes a list, ["name", "title"], and uses the first field that has a value.

Lazy loading ​

vue-treeselect sends every load through loadOptions. VPick splits it in two. Branches that load on expand still use children: null:

js
// Before
function loadOptions({ action, parentNode, callback }) {
  if (action === "LOAD_CHILDREN_OPTIONS") {
    api.children(parentNode.id).then((children) => {
      parentNode.children = children
      callback()
    })
  }
}

// After: return the children, VPick puts them in place
function loadChildren(option) {
  return api.children(option.id)
}

Server search with async becomes fetch-options, which gets the query and an AbortSignal for requests that go stale:

js
// After
function fetchOptions(query, { signal }) {
  return api.search(query, { signal })
}

Loading the root options from loadOptions (LOAD_ROOT_OPTIONS) has no equivalent. Fetch them before rendering and pass them as options.

Not supported ​

These have no VPick equivalent: limit, limitText, showCount, showCountOf, branchNodesFirst, openOnClick, openOnFocus, cacheOptions, defaultOptions, autoLoadRootOptions, allowClearingDisabled, allowSelectingDisabledDescendants, autoSelectAncestors, autoSelectDescendants, autoDeselectAncestors, autoDeselectDescendants, beforeClearAll, clearAllText, clearValueText, tabIndex and disableFuzzyMatching.

Search matches parts of words and ignores accents, but it is not fuzzy. For typo-tolerant search, see Fuse.js.

Events ​

vue-treeselectVPick
inputv-model (input on Vue 2.7, update:modelValue on Vue 3)
select(node, instanceId)select(option), your original option object
deselect(node, instanceId)deselect(option)
open(instanceId)open
close(value, instanceId)close
search-change(query, instanceId)search(query)

Slots ​

vue-treeselectVPick
option-label with { node, count, ... }option-label with { option, isBranch, isExpanded, depth }
value-label with { node }value-label with { option }
before-list, after-listNot supported

In both slots, option.raw is the object you passed in, so fields beyond the label are still there. See Slots.

Styling ​

The vue-treeselect__* classes do not exist in VPick, so CSS that targets them needs rewriting. Most of it becomes a CSS variable. See Theming and Styling branch and leaf rows.

Tests or CSS that select an option by data-id can keep working with optionAttrs:

vue
<VPick :option-attrs="(o) => ({ 'data-id': o.value })" />

Released under the MIT License.