Skip to content

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