Skip to content
Accessibility: Full
Translations: Not Needed
Migration Guide: Not Needed

Sidebar

A flexible sidebar layout component that supports both inline and overlay modes with customizable positioning and tab integration.

Default

Pass position="left" to position HLTabs on the left

Sample Sidebar-adjacent Content

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

Dragon Ball

Last updated 2m ago

Configure Tab Content
Lorem Ipsum is simply dummy text of the printing and typesetting industry.

Sample Sidebar-adjacent Content

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

Dragon Ball

Last updated 2m ago

Configure Tab Content
Lorem Ipsum is simply dummy text of the printing and typesetting industry.
SidebarContainer.vue
vue
<template>
  <HLSidebarContainer
    id="default-sidebar"
    sidebarWidth="320px"
    ref="sidebarContainerRef"
    v-model:overlay-popover-props="overlayPopoverProps"
  >
    <div class="p-4 h-full">
      <h1 class="m-0">Sample Sidebar-adjacent Content</h1>
      <span>
        Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text
        ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived
        not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the
        1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like
        Aldus PageMaker including versions of Lorem Ipsum.
      </span>
    </div>
    <template #tabs>
      <div class="h-full flex flex-col justify-between items-center" :class="$props.position === 'left' ? 'pr-2' : 'pl-2'">
        <HLTabs id="just-tabs-demo" sidebar size="md" type="line" @update:value="handleTabChange">
          <HLTab name="configure-tab" tab-key="configure" tab="Configure" icon-only>
            <HLIcon :size="18">
              <CreditCard01Icon />
            </HLIcon>
          </HLTab>
          <HLTab name="payment-tab" tab-key="payment" tab="Payment" icon-only>
            <HLIcon :size="18">
              <Tool02Icon />
            </HLIcon>
          </HLTab>
          <HLTab name="pin-tab" tab-key="pin" tab="Pin" icon-only>
            <HLIcon :size="18">
              <Pin02Icon />
            </HLIcon>
          </HLTab>
        </HLTabs>
        <div>
          <HLDivider class="!my-2" />
          <HLPopover content-class="p-2">
            <template #trigger>
              <HLButton id="btn-end-action" variant="ghost">
                <template #icon>
                  <HLIcon :size="18">
                    <ArchiveIcon />
                  </HLIcon>
                </template>
              </HLButton>
            </template>
            Sample Custom End-Action
          </HLPopover>
        </div>
      </div>
    </template>
    <template #sidebar-content>
      <div class="p-4 flex flex-col gap-4 bg-white h-full justify-between">
        <HLHeaderLite
          id="header-lite-sidebar"
          title="Dragon Ball"
          subtitle="Last updated 2m ago"
          size="lg"
          :closable="true"
          @update:close="handleClose"
        />
        <div v-if="selectedTab === 'configure-tab'" class="h-full">
          <div style="font-weight: 700">Configure Tab Content</div>
          <span> Lorem Ipsum is simply dummy text of the printing and typesetting industry. </span>
        </div>
        <div v-else-if="selectedTab === 'payment-tab'" class="h-full">
          <div style="font-weight: 700">Payment Tab Content</div>
          <span> Lorem Ipsum is simply dummy text of the printing and typesetting industry. </span>
        </div>
        <div v-else-if="selectedTab === 'pin-tab'" class="h-full">
          <div style="font-weight: 700">Pin Tab Content</div>
          <span> Lorem Ipsum is simply dummy text of the printing and typesetting industry. </span>
        </div>
        <HLSectionFooter id="footer-1" :top-padding="false" :bottom-padding="false" :horizontal-padding="false">
          <HLSectionFooterItem justify="start">
            <HLButton id="btn-1" size="2xs" variant="text" link>Learn more</HLButton>
          </HLSectionFooterItem>
          <HLSectionFooterItem justify="end">
            <HLButton id="secondary" size="2xs" variant="secondary" @click="handleClose"> Cancel </HLButton>
            <HLButton id="primary" size="2xs" variant="primary" color="blue" @click="handleClose"> Save </HLButton>
          </HLSectionFooterItem>
        </HLSectionFooter>
      </div>
    </template>
  </HLSidebarContainer>
</template>

<script setup lang="ts">
import { ArchiveIcon, CreditCard01Icon, Pin02Icon, Tool02Icon } from '@gohighlevel/ghl-icons/24/outline'
import { reactive, ref } from 'vue'
import {
  HLButton,
  HLHeaderLite,
  HLIcon,
  HLPopover,
  HLSectionFooter,
  HLSectionFooterItem,
  HLSidebarContainer,
  HLTab,
  HLTabs,
  type HLTabsSize,
  type HLTabsType,
} from '@platform-ui/highrise'

const overlayPopoverProps = reactive({
  show: false,
  'on-clickoutside': () => {
    overlayPopoverProps.show = false
  },
})

const sidebarContainerRef = ref(null)
const selectedTab = ref('configure-tab')

const handleTabChange = (tab: string) => {
  selectedTab.value = tab
  if (props.overlay) {
    overlayPopoverProps.show = true
  } else {
    sidebarContainerRef.value?.inline.openSidebar()
  }
}

const handleClose = () => {
  if (props.overlay) {
    overlayPopoverProps.show = false
  } else {
    sidebarContainerRef.value?.inline.closeSidebar()
  }
}
</script>

Segment Type Tabs

Sample Sidebar-adjacent Content

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

Dragon Ball

Last updated 2m ago

Configure Tab Content
Lorem Ipsum is simply dummy text of the printing and typesetting industry.
SidebarContainer.vue
vue
<template>
  <HLSidebarContainer
    id="default-sidebar"
    sidebarWidth="320px"
    ref="sidebarContainerRef"
    v-model:overlay-popover-props="overlayPopoverProps"
  >
    <div class="p-4 h-full">
      <h1 class="m-0">Sample Sidebar-adjacent Content</h1>
      <span>
        Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text
        ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived
        not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the
        1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like
        Aldus PageMaker including versions of Lorem Ipsum.
      </span>
    </div>
    <template #tabs>
      <div class="h-full flex flex-col justify-between items-center" :class="$props.position === 'left' ? 'pr-2' : 'pl-2'">
        <HLTabs id="just-tabs-demo" sidebar size="md" type="segment" @update:value="handleTabChange">
          <HLTab name="configure-tab" tab-key="configure" tab="Configure" icon-only>
            <HLIcon :size="18">
              <CreditCard01Icon />
            </HLIcon>
          </HLTab>
          <HLTab name="payment-tab" tab-key="payment" tab="Payment" icon-only>
            <HLIcon :size="18">
              <Tool02Icon />
            </HLIcon>
          </HLTab>
          <HLTab name="pin-tab" tab-key="pin" tab="Pin" icon-only>
            <HLIcon :size="18">
              <Pin02Icon />
            </HLIcon>
          </HLTab>
        </HLTabs>
        <div>
          <HLDivider class="!my-2" />
          <HLPopover content-class="p-2">
            <template #trigger>
              <HLButton id="btn-end-action" variant="ghost">
                <template #icon>
                  <HLIcon :size="18">
                    <ArchiveIcon />
                  </HLIcon>
                </template>
              </HLButton>
            </template>
            Sample Custom End-Action
          </HLPopover>
        </div>
      </div>
    </template>
    <template #sidebar-content>
      <div class="p-4 flex flex-col gap-4 bg-white h-full justify-between">
        <HLHeaderLite
          id="header-lite-sidebar"
          title="Dragon Ball"
          subtitle="Last updated 2m ago"
          size="lg"
          :closable="true"
          @update:close="handleClose"
        />
        <div v-if="selectedTab === 'configure-tab'" class="h-full">
          <div style="font-weight: 700">Configure Tab Content</div>
          <span> Lorem Ipsum is simply dummy text of the printing and typesetting industry. </span>
        </div>
        <div v-else-if="selectedTab === 'payment-tab'" class="h-full">
          <div style="font-weight: 700">Payment Tab Content</div>
          <span> Lorem Ipsum is simply dummy text of the printing and typesetting industry. </span>
        </div>
        <div v-else-if="selectedTab === 'pin-tab'" class="h-full">
          <div style="font-weight: 700">Pin Tab Content</div>
          <span> Lorem Ipsum is simply dummy text of the printing and typesetting industry. </span>
        </div>
        <HLSectionFooter id="footer-1" :top-padding="false" :bottom-padding="false" :horizontal-padding="false">
          <HLSectionFooterItem justify="start">
            <HLButton id="btn-1" size="2xs" variant="text" link>Learn more</HLButton>
          </HLSectionFooterItem>
          <HLSectionFooterItem justify="end">
            <HLButton id="secondary" size="2xs" variant="secondary" @click="handleClose"> Cancel </HLButton>
            <HLButton id="primary" size="2xs" variant="primary" color="blue" @click="handleClose"> Save </HLButton>
          </HLSectionFooterItem>
        </HLSectionFooter>
      </div>
    </template>
  </HLSidebarContainer>
</template>

<script setup lang="ts">
import { ArchiveIcon, CreditCard01Icon, Pin02Icon, Tool02Icon } from '@gohighlevel/ghl-icons/24/outline'
import { reactive, ref } from 'vue'
import {
  HLButton,
  HLHeaderLite,
  HLIcon,
  HLPopover,
  HLSectionFooter,
  HLSectionFooterItem,
  HLSidebarContainer,
  HLTab,
  HLTabs,
  type HLTabsSize,
  type HLTabsType,
} from '@platform-ui/highrise'

const overlayPopoverProps = reactive({
  show: false,
  'on-clickoutside': () => {
    overlayPopoverProps.show = false
  },
})

const sidebarContainerRef = ref(null)
const selectedTab = ref('configure-tab')

const handleTabChange = (tab: string) => {
  selectedTab.value = tab
  if (props.overlay) {
    overlayPopoverProps.show = true
  } else {
    sidebarContainerRef.value?.inline.openSidebar()
  }
}

const handleClose = () => {
  if (props.overlay) {
    overlayPopoverProps.show = false
  } else {
    sidebarContainerRef.value?.inline.closeSidebar()
  }
}
</script>

Overlay Variant with hover Trigger

Trigger can be customized from between click (default) and hover

Sample Sidebar-adjacent Content

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
SidebarContainer.vue
vue
<template>
  <HLSidebarContainer
    ref="sidebarContainerRef"
    id="default-sidebar"
    sidebarWidth="320px"
    overlay
    trigger="hover"
    :collapsed="false"
    v-model:overlay-popover-props="overlayPopoverProps"
  >
    <div class="p-4 h-full">
      <h1 class="m-0">Sample Sidebar-adjacent Content</h1>
      <span>
        Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text
        ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived
        not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the
        1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like
        Aldus PageMaker including versions of Lorem Ipsum.
      </span>
    </div>
    <template #tabs>
      <div class="h-full flex flex-col justify-between items-center" :class="$props.position === 'left' ? 'pr-2' : 'pl-2'">
        <HLTabs id="just-tabs-demo" sidebar size="md" type="segment" @update:value="handleTabChange">
          <HLTab name="configure-tab" tab-key="configure" tab="Configure" icon-only>
            <HLIcon :size="18">
              <CreditCard01Icon />
            </HLIcon>
          </HLTab>
          <HLTab name="payment-tab" tab-key="payment" tab="Payment" icon-only>
            <HLIcon :size="18">
              <Tool02Icon />
            </HLIcon>
          </HLTab>
          <HLTab name="pin-tab" tab-key="pin" tab="Pin" icon-only>
            <HLIcon :size="18">
              <Pin02Icon />
            </HLIcon>
          </HLTab>
        </HLTabs>
        <div>
          <HLDivider class="!my-2" />
          <HLPopover content-class="p-2">
            <template #trigger>
              <HLButton id="btn-end-action" variant="ghost">
                <template #icon>
                  <HLIcon :size="18">
                    <ArchiveIcon />
                  </HLIcon>
                </template>
              </HLButton>
            </template>
            Sample Custom End-Action
          </HLPopover>
        </div>
      </div>
    </template>
    <template #sidebar-content>
      <div class="p-4 flex flex-col gap-4 bg-white h-full justify-between">
        <HLHeaderLite
          id="header-lite-sidebar"
          title="Dragon Ball"
          subtitle="Last updated 2m ago"
          size="lg"
          :closable="true"
          @update:close="handleClose"
        />
        <div v-if="selectedTab === 'configure-tab'" class="h-full">
          <div style="font-weight: 700">Configure Tab Content</div>
          <span> Lorem Ipsum is simply dummy text of the printing and typesetting industry. </span>
        </div>
        <div v-else-if="selectedTab === 'payment-tab'" class="h-full">
          <div style="font-weight: 700">Payment Tab Content</div>
          <span> Lorem Ipsum is simply dummy text of the printing and typesetting industry. </span>
        </div>
        <div v-else-if="selectedTab === 'pin-tab'" class="h-full">
          <div style="font-weight: 700">Pin Tab Content</div>
          <span> Lorem Ipsum is simply dummy text of the printing and typesetting industry. </span>
        </div>
        <HLSectionFooter id="footer-1" :top-padding="false" :bottom-padding="false" :horizontal-padding="false">
          <HLSectionFooterItem justify="start">
            <HLButton id="btn-1" size="2xs" variant="text" link>Learn more</HLButton>
          </HLSectionFooterItem>
          <HLSectionFooterItem justify="end">
            <HLButton id="secondary" size="2xs" variant="secondary" @click="handleClose"> Cancel </HLButton>
            <HLButton id="primary" size="2xs" variant="primary" color="blue" @click="handleClose"> Save </HLButton>
          </HLSectionFooterItem>
        </HLSectionFooter>
      </div>
    </template>
  </HLSidebarContainer>
</template>

<script setup lang="ts">
import { ArchiveIcon, CreditCard01Icon, Pin02Icon, Tool02Icon } from '@gohighlevel/ghl-icons/24/outline'
import { reactive, ref } from 'vue'
import {
  HLButton,
  HLHeaderLite,
  HLIcon,
  HLPopover,
  HLSectionFooter,
  HLSectionFooterItem,
  HLSidebarContainer,
  HLTab,
  HLTabs,
  type HLTabsSize,
  type HLTabsType,
} from '@platform-ui/highrise'

const overlayPopoverProps = reactive({
  show: false,
  'on-clickoutside': () => {
    overlayPopoverProps.show = false
  },
})

const sidebarContainerRef = ref(null)
const selectedTab = ref('configure-tab')

const handleTabChange = (tab: string) => {
  selectedTab.value = tab
  if (props.overlay) {
    overlayPopoverProps.show = true
  } else {
    sidebarContainerRef.value?.inline.openSidebar()
  }
}

const handleClose = () => {
  if (props.overlay) {
    overlayPopoverProps.show = false
  } else {
    sidebarContainerRef.value?.inline.closeSidebar()
  }
}
</script>

Manual Control for Custom Implementations

SidebarContainer.vue
vue
<template>
  <HLSidebarContainer
    ref="sidebarContainerRef"
    id="default-sidebar"
    sidebarWidth="320px"
    overlay
    trigger="hover"
    :collapsed="false"
    v-model:overlay-popover-props="overlayPopoverProps"
    v-model:collapsed="collapsedRef"
  >
    <div class="p-4 h-full">
      <h1 class="m-0">Sample Sidebar-adjacent Content</h1>
      <span>
        Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text
        ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived
        not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the
        1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like
        Aldus PageMaker including versions of Lorem Ipsum.
      </span>
    </div>
    <template #tabs>
      <HLTabs
        :id="`just-tabs-demo-${$props.id}`"
        ref="tabsRef"
        v-model:value="selectedTab"
        sidebar
        :type="$attrs.tabType as HLTabsType"
        :size="$attrs.tabSize as HLTabsSize"
        trigger="manual"
      >
        <HLTab name="configure-tab" tab-key="configure" tab="Configure" icon-only @click="handleTabClick('configure-tab')">
          <HLIcon :size="18">
            <CreditCard01Icon />
          </HLIcon>
        </HLTab>
        <HLTab name="payment-tab" tab-key="payment" tab="Payment" icon-only @click="handleTabClick('payment-tab')">
          <HLIcon :size="18">
            <Tool02Icon />
          </HLIcon>
        </HLTab>
        <HLTab name="pin-tab" tab-key="pin" tab="Pin" icon-only @click="handleTabClick('pin-tab')">
          <HLIcon :size="18">
            <Pin02Icon />
          </HLIcon>
        </HLTab>
      </HLTabs>
    </template>
    <template #sidebar-content>
      <div class="p-4 flex flex-col gap-4 bg-white h-full justify-between">
        <HLHeaderLite
          id="header-lite-sidebar"
          title="Dragon Ball"
          subtitle="Last updated 2m ago"
          size="lg"
          :closable="true"
          @update:close="handleClose"
        />
        <div v-if="selectedTab === 'configure-tab'" class="h-full">
          <div style="font-weight: 700">Configure Tab Content</div>
          <span> Lorem Ipsum is simply dummy text of the printing and typesetting industry. </span>
        </div>
        <div v-else-if="selectedTab === 'payment-tab'" class="h-full">
          <div style="font-weight: 700">Payment Tab Content</div>
          <span> Lorem Ipsum is simply dummy text of the printing and typesetting industry. </span>
        </div>
        <div v-else-if="selectedTab === 'pin-tab'" class="h-full">
          <div style="font-weight: 700">Pin Tab Content</div>
          <span> Lorem Ipsum is simply dummy text of the printing and typesetting industry. </span>
        </div>
        <HLSectionFooter id="footer-1" :top-padding="false" :bottom-padding="false" :horizontal-padding="false">
          <HLSectionFooterItem justify="start">
            <HLButton id="btn-1" size="2xs" variant="text" link @click="handleClose">Learn more</HLButton>
          </HLSectionFooterItem>
          <HLSectionFooterItem justify="end">
            <HLButton id="secondary" size="2xs" variant="secondary" @click="handleClose"> Cancel </HLButton>
            <HLButton id="primary" size="2xs" variant="primary" color="blue" @click="handleClose"> Save </HLButton>
          </HLSectionFooterItem>
        </HLSectionFooter>
      </div>
    </template>
  </HLSidebarContainer>
</template>

<script setup lang="ts">
import { CreditCard01Icon, Pin02Icon, Tool02Icon } from '@gohighlevel/ghl-icons/24/outline'
import { reactive, ref } from 'vue'
import {
  HLButton,
  HLHeaderLite,
  HLIcon,
  HLSectionFooter,
  HLSectionFooterItem,
  HLSidebarContainer,
  HLTab,
  HLTabs,
  type HLTabsSize,
  type HLTabsType,
} from '@platform-ui/highrise'

const overlayPopoverProps = reactive({
  show: false,
  'on-clickoutside': () => {
    overlayPopoverProps.show = false
  },
})

const sidebarContainerRef = ref(null)
const selectedTab = ref('configure-tab')

const collapsedRef = ref(true)
const tabsRef = ref(null)

const handleTabClick = (tab: string) => {
  console.log('tabclick')
  if (selectedTab.value === tab) collapsedRef.value = !collapsedRef.value
  else collapsedRef.value = false
  selectedTab.value = tab
  tabsRef.value?.syncBarPosition(tab)
}

const handleClose = () => {
  if (props.overlay) {
    overlayPopoverProps.show = false
  } else {
    sidebarContainerRef.value?.inline.closeSidebar()
  }
}
</script>

Props

NameTypeDefaultDescription
idstring-The id of the element
sidebarWidthstring | number280Width of the sidebar (pixels or CSS units)
classNamestringundefinedAdditional CSS class names
collapsedbooleanfalseWhether the sidebar is collapsed (controlled)
overlaybooleanfalseWhether to use overlay mode instead of inline
overlayPopoverPropsHLPopoverPropsundefinedProps to pass to the underlying popover (only overlay mode)
position'left' | 'right''right'Position of the sidebar
trigger'click' | 'hover''click'Trigger type

Emits

NameParametersDescription
@update:collapsed(collapsed: boolean)Emitted when sidebar collapsed state changes

Slots

NameParametersDescription
default()Main content area
tabs()Tab navigation area
sidebar-content()Content displayed inside the sidebar

Exposed Methods

Inline Mode Methods

MethodParametersReturn TypeDescription
inline.isCollapsed()()booleanReturns the collapsed boolean value
inline.openSidebar()()voidOpen the sidebar
inline.closeSidebar()()voidClose the sidebar
inline.toggleSidebar()()voidToggle sidebar open / closed

Overlay Mode Methods

MethodParametersReturn TypeDescription
overlay.getPopoverRef()()Ref<HLPopover>Returns the ref to the HLPopover component in overlay mode