Skip to content

Migrating from ghl-ui AdvancedSelect to HighRise AdvancedSelect

UIAdvancedSelect maps directly to HLAdvancedSelect. The API is broadly similar — searchable dropdown with single/multiple/tag modes, custom triggers, remote search, and tag creation — but the option model, prop defaults, and event names have changed. The biggest schema shift: HighRise options use key and src (inherited from HLDropdownOption) instead of value and imageUrl.

Component Implementation Changes

Import Changes

diff
- import { UIAdvancedSelect } from '@gohighlevel/ghl-ui'
+ import { HLAdvancedSelect } from '@platform-ui/highrise'

Basic Usage Changes

diff
- <UIAdvancedSelect
-   :value="selectedValue"
-   :options="options"
-   trigger-placeholder="Select a person"
-   @onChange="v => selectedValue = v"
- />
+ <HLAdvancedSelect
+   :value="selectedValue"
+   :options="options"
+   trigger-placeholder="Select a person"
+   @update:value="v => selectedValue = v"
+ />

Option Model Changes

diff
- const options = [
-   { label: 'John Doe', value: '1', imageUrl: 'https://example.com/john.jpg' },
-   { label: 'Jane Smith', value: '2' },
- ]
+ const options = [
+   { label: 'John Doe', key: '1', type: 'avatar', src: 'https://example.com/john.jpg' },
+   { label: 'Jane Smith', key: '2', type: 'avatar' },
+ ]

HLAdvancedSelectOption extends HLDropdownOption:

  • valuekey (unique identifier)
  • imageUrlsrc
  • Add type: 'avatar' for avatar rendering with initials fallback; omit for text-only rows.
  • children (for grouped options) and meta (arbitrary per-option data) still supported.

Props Changes

ghl-ui PropHighRise PropNotes
valuevalue (+ @update:value)Same shape (string | number | (string | number)[] | null); event renamed from onChange.
optionsoptionsSame array shape, but option fields changed — see above.
multiplemultipleSame.
selectTypeselectTypeSame: 'default' | 'tags'.
remoteremoteSame; pair with @search.
loadingloadingSame.
showSearchshowSearchSame.
searchPlaceholdersearchPlaceholderSame.
triggerPlaceholdertriggerPlaceholderSame.
maxTagCountmaxTagCountSame — accepts a number to cap visible tags.
virtualScrollvirtualScrollSame — pair with popoverWidth for stable dropdown sizing.
placementplacementSame. Default is 'bottom-start'.
showshow (+ @update:show)Controlled visibility; event renamed from onClose.
totoSame teleport target.
showAddTagCTAshowAddTagCTASame; only active in selectType="tags".
handleNewOptionRemotehandleNewOptionRemoteSame; emits @newTag instead of onNewTag.
disableddisabledSame.
resetSearchStringresetSearchStringSame.
popoverWidthpopoverWidthSame: number | 'trigger'.
popoverContainerClasspopoverContainerClassSame.
renderOption#option-renderer slotProp replaced by slot — see Custom Option Rendering below.
randomColorRemoved. Trigger avatars use HLAvatar's default name-based coloring; no toggle.
sortSelectedValuesdockSelectedToTopRenamed; default flipped to false. Set :dock-selected-to-top="true" to restore the V1 default.
trigger ('click' | 'hover' | 'focus' | 'manual')Removed. Trigger interaction is always click-based.
filterTagsRemoved. Built-in case-insensitive substring filter; use remote + @search for custom logic.
maxAvatarCountNew: cap avatars in the trigger before collapsing to +N (default 5).
maxHeightNew: dropdown options area max height (default '320px').
defaultTagPropsNew: Partial<HLTagProps> applied to tags in the InputTag trigger (e.g. { round: true }).
defaultAvatarPropsNew: Partial<HLAvatarProps> applied to the trigger's AvatarGroup.
showArrowNew: optional popover arrow (default false).

Emits Changes

All events were renamed from onX (camelCase) to Vue's update:X / kebab-case convention.

ghl-ui EventHighRise EventNotes
@onChange@update:valueSame payload. Enables v-model:value.
@onSearch@searchSame payload.
@onScroll@scrollSame payload { clientHeight, scrollHeight, scrollTop }.
@onClose@close (or @update:show with false)Both available; prefer @update:show for controlled visibility.
@onNewTag@newTagSame payload (the typed label).
@onChangeSrcRemoved. V1 emitted 'DROPDOWN' or 'TRIGGER' to indicate which UI caused a selection change; V2 does not distinguish — wrap @update:value with your own source tracking if you need it.

Slots Changes

ghl-ui SlotHighRise SlotNotes
#trigger#triggerSame; receives { disabled, value, placeholder }.
#empty#emptySame.
#emptySearch#emptySearchSame; receives { searchString }.
#action-top#headerReplaced. Position differs: V1's #action-top sat next to the search input; V2's #header renders above the search input as sticky content.
#action-bottom#footerReplaced. Renders below the options list.
renderOption (prop)#option-rendererProp → slot; receives { option } where option.selected reflects current selection. The slot fully replaces the default row, including its click handler — wire @click yourself.
#loaderNew: content at the bottom of the options list (use for infinite scroll).

Examples

vue
<template>
  <HLAdvancedSelect
    :options="remoteOptions"
    :value="selected"
    :loading="loading"
    multiple
    remote
    trigger-placeholder="Search people"
    @update:value="v => selected = v"
    @search="onSearch"
  />
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { HLAdvancedSelect } from '@platform-ui/highrise'

const selected = ref<(string | number)[]>([])
const loading = ref(false)
const remoteOptions = ref([
  { label: 'Alice', key: 'alice', type: 'avatar' },
  { label: 'Bob', key: 'bob', type: 'avatar' },
])

const onSearch = async (query: string) => {
  loading.value = true
  // fetch and set remoteOptions.value
  loading.value = false
}
</script>

Tags Mode with Create

vue
<template>
  <HLAdvancedSelect
    :options="tagOptions"
    :value="tagValue"
    select-type="tags"
    :show-add-tag-c-t-a="true"
    :reset-search-string="true"
    trigger-placeholder="Add or create tags"
    search-placeholder="Search or create tags"
    :default-tag-props="{ round: true }"
    @update:value="v => tagValue = v"
  />
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { HLAdvancedSelect } from '@platform-ui/highrise'

const tagValue = ref<string[]>([])
const tagOptions = ref([
  { label: 'Bug', key: 'bug' },
  { label: 'Feature', key: 'feature' },
])
</script>

Custom Option Rendering

renderOption (a prop returning VNodes) is replaced by the #option-renderer slot. The slot receives { option } with option.selected already resolved, and replaces the default row entirely — including click handling.

diff
- <UIAdvancedSelect
-   :options="options"
-   :value="value"
-   :render-option="renderOption"
-   @onChange="v => value = v"
- />
- <script setup>
-   const renderOption = (option, isActive) => h('div', {
-     class: isActive ? 'bg-primary-50' : ''
-   }, option.label)
- </script>
+ <HLAdvancedSelect
+   :options="options"
+   :value="value"
+   @update:value="v => value = v"
+ >
+   <template #option-renderer="{ option }">
+     <div
+       :class="option.selected ? 'bg-primary-50' : ''"
+       @click="toggle(option.key)"
+     >
+       {{ option.label }}
+     </div>
+   </template>
+ </HLAdvancedSelect>

Breaking Changes

  1. Option identifiervaluekey. Update both option arrays and any code that reads from options (e.g. .find(o => o.value === ...).find(o => o.key === ...)).
  2. Avatar image fieldimageUrlsrc; add type: 'avatar' to opt into avatar rendering.
  3. Event namesonChange/onSearch/onScroll/onClose/onNewTagupdate:value/search/scroll/close/newTag. onChangeSrc is gone.
  4. renderOption prop → #option-renderer slot — slot fully replaces the default row, including its click handler.
  5. sortSelectedValuesdockSelectedToTop — renamed and default flipped to false.
  6. randomColor, trigger, filterTags removed — no replacement; rely on built-in defaults or use remote for custom search.
  7. Action slot rename#action-top#header, #action-bottom#footer.

Best Practices

  1. Always use key (not value) as the unique identifier on options.
  2. Set type: 'avatar' on options that need image/initials rendering.
  3. Use remote + @search for server-side filtering; update options with API results.
  4. Enable virtual-scroll for lists over ~50 options and always pair with a fixed popoverWidth to keep the dropdown size stable.
  5. Use #header/#footer slots for sticky content (counts, action buttons).
  6. Prefer v-model:value and v-model:show over manual event wiring.

TypeScript Support

ts
import type {
  HLAdvancedSelectProps,
  HLAdvancedSelectOption,
  HLAdvancedSelectValue,
} from '@platform-ui/highrise'

interface HLAdvancedSelectOption extends HLDropdownOption {
  children?: HLAdvancedSelectOption[]
  meta?: any
}