Migrating from ghl-ui DropdownTree to HighRise Dropdown (tree mode)
This guide helps you move from UIDropdownTree to HLDropdown with tree-mode enabled.
Component Implementation Changes
Import Changes
diff
- import { UIDropdownTree } from '@gohighlevel/ghl-ui'
+ import { HLDropdown, HLButton } from '@platform-ui/highrise'Basic Usage Changes
diff
- <UIDropdownTree
- v-model="selectedValue"
- :options="options"
- label="Select Option"
- @on-select="handleSelect"
- />
+ <HLDropdown
+ id="tree-dropdown"
+ :options="options"
+ tree-mode
+ trigger="click"
+ placement="bottom-start"
+ :show-search="true"
+ @select="handleSelect"
+ >
+ <HLButton size="sm">Select Option</HLButton>
+ </HLDropdown>Props Changes
| ghl-ui Prop | HighRise Prop / Pattern | Notes |
|---|---|---|
v-model | @select + local state | HL emits select(key, option); manage selected value yourself. |
options | options | HLDropdownOption[] supports children for tree mode. |
label | Trigger content | Provide button text/slot in the trigger. |
placeholder | Trigger content | Same as above. |
id | id (optional) | Auto-generated if omitted. |
trigger | trigger | 'click' (default), 'hover', 'focus'. |
placement | placement | Default bottom-start; RTL-aware. |
width | width | Number or 'auto'; default 182. |
tree-mode | tree-mode | Enables hierarchical navigation. |
show-search | show-search | Built-in search row (default true). |
| - | closeOnSelect | Close after selection (default true). |
| - | multiple | Multi-select support (default false). |
| - | searchValue / @update:searchValue | Control search input if needed. |
Options Format (tree)
Use the same HLDropdownOption shape as standard dropdowns, with children arrays for nesting.
Examples
Basic Tree Dropdown
vue
<template>
<HLDropdown id="basic-tree" :options="options" tree-mode :show-search="true" :width="240">
<HLButton size="sm">Choose location</HLButton>
</HLDropdown>
</template>
<script setup>
const options = [
{
key: 'locations',
label: 'Locations',
children: [
{ key: 'usa', label: 'USA' },
{ key: 'india', label: 'India' },
],
},
]
</script>Tree Dropdown with Form Validation
vue
<template>
<HLForm :model="form" :rules="rules">
<HLFormItem label="Category" path="category" required>
<HLDropdown id="category-dropdown" :options="categories" tree-mode :show-search="true" :width="260" @select="handleSelect">
<HLButton size="sm">{{ form.category || 'Select Category' }}</HLButton>
</HLDropdown>
</HLFormItem>
</HLForm>
</template>
<script setup>
import { reactive } from 'vue'
const form = reactive({ category: '' })
const categories = [
{ key: 'fruits', label: 'Fruits', children: [{ key: 'apple', label: 'Apple' }] },
{ key: 'vegetables', label: 'Vegetables', children: [{ key: 'carrot', label: 'Carrot' }] },
]
const handleSelect = (key, option) => {
form.category = option.label
}
const rules = {
category: [{ required: true, message: 'Please select a category', trigger: 'change' }],
}
</script>Custom Search Handling
vue
<template>
<HLDropdown id="custom-search-tree" :options="filteredOptions" tree-mode :show-search="true" :width="260" @search="handleSearch">
<HLButton size="sm">Custom Search</HLButton>
</HLDropdown>
</template>
<script setup>
import { computed, ref } from 'vue'
const search = ref('')
const allOptions = [
{
key: 'colors',
label: 'Colors',
children: [
{ key: 'red', label: 'Red' },
{ key: 'blue', label: 'Blue' },
],
},
]
const filteredOptions = computed(() => {
if (!search.value) return allOptions
const term = search.value.toLowerCase()
return allOptions
.map(group => {
const children = group.children?.filter(item => item.label.toLowerCase().includes(term))
return children?.length ? { ...group, children } : null
})
.filter(Boolean)
})
const handleSearch = value => {
search.value = value
}
</script>Best Practices
- Provide an explicit
idwhen tests need deterministic selectors. - Keep option keys stable; tree navigation relies on them.
- Use
showSearchfor discoverability; disable when the tree is small. - Combine
multiplewithshowSelectedMarkfor multi-select scenarios. - Consider
width='auto'to match trigger width when embedding in tight layouts.