Skip to content

Migrating from UITabs to HighRise Tabs

HighRise HLTabs supports the same core line/segment styles as UITabs while adding overflow handling and richer slot hooks. HLTabPane remains the primary way to declare tabs; HLTab is available when you need header-only tabs.

Component Implementation Changes

Import Changes

diff
- import { UITabs, UITab, UITabPane } from '@gohighlevel/ghl-ui'
+ import { HLTabs, HLTab, HLTabPane, renderTabWithIcon } from '@platform-ui/highrise'

Key Differences

  • Tab types: only 'line' and 'segment' are supported (card/bar are removed).
  • animated prop is removed; transitions are handled internally.
  • Size tokens change from small|medium|large to sm|md|lg|xl|2xl (default md).
  • Optional overflow handling via maxVisibleTabs with overflow / end-actions slots.
  • id is optional (use when you need anchors or analytics), not required.

Props Mapping

ghl-ui PropHighRise PropNotes
value / v-model:valuevalue / v-model:valueSame binding pattern
defaultValuedefault-valueSame behavior
type (line | segment | bar | card)type (line | segment)Card/bar removed; pick line or segment
placement (top | right | bottom | left)placementSame values
justifyContentjustify-contentSame values
size (small | medium | large)size (sm | md | lg | xl | 2xl)Choose closest match (default md)
animatedRemoved; transitions handled internally
maxVisibleTabsEnables overflow handling and overflow slot
theme, noBorder, offSetStyling helpers for HighRise tabs
id (optional)id (optional)Use when you need anchors, analytics, or testing hooks

Slots

  • Existing prefix / suffix slots remain.
  • New: overflow (custom overflow trigger) and end-actions (actions on the right).
  • Icons: prefer renderTabWithIcon to keep spacing consistent.

Examples

Basic Tabs

vue
<template>
  <HLTabs v-model:value="activeTab">
    <HLTabPane name="tab1" tab="Tab 1">Content for Tab 1</HLTabPane>
    <HLTabPane name="tab2" tab="Tab 2">Content for Tab 2</HLTabPane>
    <HLTabPane name="tab3" tab="Tab 3">Content for Tab 3</HLTabPane>
  </HLTabs>
</template>

<script setup>
import { ref } from 'vue'

const activeTab = ref('tab1')
</script>

Tabs with Icons

vue
<template>
  <HLTabs>
    <HLTabPane name="payment" :tab="renderTabWithIcon({ tab: 'Payment', icon: CreditCard01Icon, tabSize: 'md', iconOnly: false })">
      Payment content
    </HLTabPane>
    <HLTabPane name="settings" :tab="renderTabWithIcon({ tab: 'Settings', icon: Settings01Icon, tabSize: 'md', iconOnly: false })">
      Settings content
    </HLTabPane>
  </HLTabs>
</template>

<script setup>
import { CreditCard01Icon, Settings01Icon } from '@gohighlevel/ghl-icons/24/outline'
import { renderTabWithIcon } from '@platform-ui/highrise'
</script>

Segment Tabs with Overflow

vue
<template>
  <HLTabs type="segment" :max-visible-tabs="3">
    <HLTabPane v-for="tab in tabs" :key="tab" :name="tab" :tab="tab"> {{ tab }} content </HLTabPane>

    <template #overflow>
      <HLButton size="3xs" variant="ghost">
        <template #icon><DotsHorizontalIcon /></template>
      </HLButton>
    </template>
  </HLTabs>
</template>

<script setup>
import { HLTabs, HLTabPane, HLButton } from '@platform-ui/highrise'
import { DotsHorizontalIcon } from '@gohighlevel/ghl-icons/24/outline'

const tabs = ['Tab 1', 'Tab 2', 'Tab 3', 'Tab 4']
</script>

Best Practices

  1. Use type="segment" for pill/segmented controls, type="line" for standard headers.
  2. Provide concise tab labels; use renderTabWithIcon for iconized tabs.
  3. Enable maxVisibleTabs and supply an overflow slot when tab counts can grow.
  4. Keep justify-content aligned with surrounding layout (start/center/end).
  5. Only set id when it is referenced externally (analytics, anchor links).

Migrating from UITabs to HighRise Tabs

This guide will help you migrate from the ghl-ui Tabs component to the new HighRise Tabs component.

Component Implementation Changes

Import Changes

diff
- import { UITabs, UITab, UITabPane } from '@gohighlevel/ghl-ui'
+ import { HLTabs, HLTabPane, renderTabWithIcon,HLTab } from '@platform-ui/highrise'

Breaking Changes

The following architectural changes have been made:

  1. Component Structure:

    • Old: Used three components (UITabs, UITab, UITabPane)
    • New: Uses two components (HLTabs, HLTab, HLTabPane)
    • Simplified architecture with better integration
  2. Tab Types:

    • Old: Supported 'line', 'card', 'bar', 'segment'
    • New: Supports 'line' and 'segment' only
    • More focused and consistent styling
  3. Tab Configuration:

    • Old: Used separate tab components
    • New: Uses structured tab objects with enhanced props
    • Better type safety and validation

Props Changes

  1. New Required Props:

    • id: Required for accessibility
  2. Renamed Props:

    • animated → Removed (animations are now built-in)
  3. New Props:

    • maxVisibleTabs: Control number of visible tabs
  4. Removed Props:

    • type='card' and type='bar' (use 'line' or 'segment')
    • animated (now built-in)

Slots Changes

  1. New Slots:

    • overflow: Custom overflow menu button
    • add-tab: Add tab button
    • end-actions: Additional actions
    • custom-slot: Custom content area
  2. Changed Slots:

    • Icon handling now uses renderTabWithIcon helper

Examples

Basic Tabs

diff
- <UITabs v-model:value="activeTab">
-   <UITabPane name="tab1" tab="Tab 1">Content for Tab 1</UITabPane>
-   <UITabPane name="tab2" tab="Tab 2">Content for Tab 2</UITabPane>
-   <UITabPane name="tab3" tab="Tab 3">Content for Tab 3</UITabPane>
- </UITabs>

+ <HLTabs
+   id="basic-tabs"
+   v-model:value="activeTab"
+ >
+   <HLTabPane name="tab1" tab="Tab 1">Content for Tab 1</HLTabPane>
+   <HLTabPane name="tab2" tab="Tab 2">Content for Tab 2</HLTabPane>
+   <HLTabPane name="tab3" tab="Tab 3">Content for Tab 3</HLTabPane>
+ </HLTabs>

UITab Usage

diff
- <UITabs v-model:value="activeTab">
-   <UITab name="tab1" key="tabkey1">Tab 1</UITab>
-   <UITab name="tab2" key="tabkey2">Tab 2</UITab>
-   <UITab name="tab3" key="tabkey3">Tab 3</UITab>
- </UITabs>

+ <HLTabs
+   id="basic-tabs"
+   v-model:value="activeTab"
+ >
+   <HLTab name="tab1" tab-key="tabkey1">Tab 1</HLTab>
+   <HLTab name="tab2" tab-key="tabkey2">Tab 2</HLTab>
+   <HLTab name="tab3" tab-key="tabkey3">Tab 3</HLTab>
+ </HLTabs>

Tabs with Icons

vue
<template>
  <HLTabs id="tabs-with-icons">
    <HLTabPane
      name="payment"
      :tab="
        renderTabWithIcon({
          iconOnly: false,
          tab: 'Payment',
          icon: CreditCard01Icon,
          tabSize: 'md',
        })
      "
    >
      Payment content
    </HLTabPane>
    <HLTabPane
      name="settings"
      :tab="
        renderTabWithIcon({
          iconOnly: false,
          tab: 'Settings',
          icon: Settings01Icon,
          tabSize: 'md',
        })
      "
    >
      Settings content
    </HLTabPane>
  </HLTabs>
</template>

<script setup>
import { HLTabs, HLTabPane, renderTabWithIcon } from '@platform-ui/highrise'
import { CreditCard01Icon, Settings01Icon } from '@gohighlevel/ghl-icons/24/outline'
</script>

Segment Style Tabs

diff
- <UITabs type="segment" v-model:value="activeTab">
-   <UITabPane name="tab1" tab="Tab 1">Content for Tab 1</UITabPane>
-   <UITabPane name="tab2" tab="Tab 2">Content for Tab 2</UITabPane>
- </UITabs>

+ <HLTabs
+   id="segment-tabs"
+   type="segment"
+   v-model:value="activeTab"
+ >
+   <HLTabPane name="tab1" tab="Tab 1">Content for Tab 1</HLTabPane>
+   <HLTabPane name="tab2" tab="Tab 2">Content for Tab 2</HLTabPane>
+ </HLTabs>

With Navigation Arrows

vue
<template>
  <HLTabs id="tabs-with-arrows" type="segment" :value="activeTab" @update:value="updateValue">
    <HLTabPane name="tab1" tab="Tab 1">Content for Tab 1</HLTabPane>
    <HLTabPane name="tab2" tab="Tab 2">Content for Tab 2</HLTabPane>
    <HLTabPane name="tab3" tab="Tab 3">Content for Tab 3</HLTabPane>

    <template #prefix>
      <ArrowLeftIcon class="w-4 h-4 cursor-pointer" @click="handleClick(true)" />
    </template>
    <template #suffix>
      <ArrowRightIcon class="w-4 h-4 cursor-pointer" @click="handleClick(false)" />
    </template>
  </HLTabs>
</template>

<script setup>
import { ref } from 'vue'
import { ArrowLeftIcon, ArrowRightIcon } from '@gohighlevel/ghl-icons/24/outline'

const activeTab = ref('tab1')
const tabKeys = ['tab1', 'tab2', 'tab3']

const handleClick = (increment = true) => {
  const len = tabKeys.length
  const index = tabKeys.indexOf(activeTab.value)
  const newIndex = index + (increment ? 1 : -1) < 0 ? len - 1 : (index + (increment ? 1 : -1)) % len
  activeTab.value = tabKeys[newIndex]
}

const updateValue = newVal => {
  activeTab.value = newVal
}
</script>

With Overflow Menu

vue
<template>
  <HLTabs id="tabs-with-overflow" type="segment" :max-visible-tabs="4">
    <HLTabPane name="tab1" tab="Tab 1">Content for Tab 1</HLTabPane>
    <HLTabPane name="tab2" tab="Tab 2">Content for Tab 2</HLTabPane>
    <HLTabPane name="tab3" tab="Tab 3">Content for Tab 3</HLTabPane>
    <HLTabPane name="tab4" tab="Tab 4">Content for Tab 4</HLTabPane>
    <HLTabPane name="tab5" tab="Tab 5">Content for Tab 5</HLTabPane>

    <template #overflow>
      <HLButton id="overflow-button" size="3xs" variant="ghost">
        <template #icon>
          <DotsHorizontalIcon />
        </template>
      </HLButton>
    </template>

    <template #end-actions>
      <HLButton id="add-tab" size="3xs" variant="ghost">
        <template #icon>
          <PlusIcon />
        </template>
      </HLButton>
    </template>
  </HLTabs>
</template>

Type Definitions

typescript
type TabPlacement = 'top' | 'right' | 'bottom' | 'left'
type TabType = 'line' | 'segment'
type TabSize = 'sm' | 'md' | 'lg'
type TabJustifyContent = 'start' | 'end' | 'space-around' | 'space-between' | 'space-evenly' | 'center'

interface TabIconConfig {
  iconOnly: boolean
  tab: string
  icon: Component
  tabSize: TabSize
}

interface HLTabsProps {
  id: string
  placement?: TabPlacement
  type?: TabType
  value?: string
  defaultValue?: string
  justifyContent?: TabJustifyContent
  size?: TabSize
  maxVisibleTabs?: number
}

Best Practices

  1. Always provide unique id props for accessibility
  2. Use appropriate tab types for different contexts
  3. Consider overflow behavior for many tabs
  4. Implement proper navigation patterns
  5. Use icons consistently across tabs
  6. Keep tab labels concise
  7. Group related content logically
  8. Consider mobile responsiveness
  9. Implement proper loading states
  10. Follow accessibility guidelines

Additional Notes

  1. The component provides built-in responsive design
  2. Enhanced overflow handling
  3. Support for custom icons
  4. Improved accessibility features
  5. Consistent styling with HighRise
  6. Support for RTL layouts
  7. Better mobile support
  8. Flexible layout options
  9. Integration with other HighRise components
  10. Built-in support for dynamic tabs

Common Migration Patterns

1. Basic Tab Replacement

The most straightforward migration involves replacing the component names and adding the required id prop:

diff
- import { UITabs, UITab, UITabPane } from '@gohighlevel/ghl-ui'
+ import { HLTabs, HLTabPane } from '@platform-ui/highrise'

- <UITabs v-model="selectedTab">
-   <UITabPane name="profile" tab="Profile">
-     Profile content
-   </UITabPane>
- </UITabs>

+ <HLTabs
+   id="profile-tabs"
+   v-model:value="selectedTab"
+   size="lg"
+ >
+   <HLTabPane
+     name="profile"
+     tab="Profile"
+   >
+     Profile content
+   </HLTabPane>
+ </HLTabs>

2. Replacing Card and Bar Types

For components using removed tab types:

diff
- <UITabs type="card">
-   <UITab name="settings">Settings</UITab>
-   <UITabPane>Settings content</UITabPane>
- </UITabs>

+ <!-- Replace card with line type -->
+ <HLTabs
+   id="settings-tabs"
+   type="segment"
+   size="lg"
+ >
+   <HLTab name="settings">Settings</HLTab>
+   <HLTabPane>Settings content</HLTabPane>
+ </HLTabs>

- <!-- Bar type replacement -->
- <UITabs type="bar">
-   <UITab name="analytics">Analytics</UITab>
-   <UITabPane>Analytics content</UITabPane>
- </UITabs>

+ <!-- Replace bar with segment type -->
+ <HLTabs
+   id="analytics-tabs"
+   type="line"
+   size="lg"
+ >
+   <HLTab name="analytics">Analytics</HLTab>
+   <HLTabPane>Analytics content</HLTabPane>
+ </HLTabs>

3. Migrating Icon Tabs

Old implementation with direct icon usage:

vue
<!-- Old implementation -->
<UITabs>
  <UITab name="dashboard">
    <template #icon>
      <DashboardIcon class="w-4 h-4" />
    </template>
    Dashboard
  </UITab>
  <UITabPane>Dashboard content</UITabPane>
</UITabs>

<!-- New implementation using renderTabWithIcon -->
<template>
  <HLTabs id="dashboard-tabs">
    <HLTabPane
      name="dashboard"
      :tab="
        renderTabWithIcon({
          iconOnly: false,
          tab: 'Dashboard',
          icon: DashboardIcon,
          tabSize: 'md',
        })
      "
    >
      Dashboard content
    </HLTabPane>
  </HLTabs>
</template>

<script setup>
import { HLTabs, HLTabPane, renderTabWithIcon } from '@platform-ui/highrise'
import { DashboardIcon } from '@gohighlevel/ghl-icons/24/outline'
</script>

4. Replacing Tab Events

Old implementation with events:

vue
<!-- Old implementation -->
<UITabs v-model="activeTab" @tab-click="handleTabClick" @tab-change="handleTabChange">
  <UITab name="events">Events</UITab>
  <UITabPane>Events content</UITabPane>
</UITabs>

<!-- New implementation -->
<template>
  <HLTabs id="events-tabs" :value="activeTab" @update:value="handleTabChange" @click="handleTabClick">
    <HLTabPane name="events" tab="Events"> Events content </HLTabPane>
  </HLTabs>
</template>

<script setup>
import { ref } from 'vue'
import { HLTabs, HLTabPane } from '@platform-ui/highrise'

const activeTab = ref('events')

const handleTabChange = newValue => {
  activeTab.value = newValue
  // Additional change handling
}

const handleTabClick = event => {
  // Click handling
}
</script>

Props Changes

size Mapping

ghl-ui sizeHighRise size
smallmd
mediumlg
largelg