Skip to content

UISegmentedControl

UISegmentedControl

A versatile segmented control component that allows users to select between multiple mutually exclusive options, with support for icons, badges, and custom styling.

<template>
<UISegmentedControl
v-model="selectedView"
:options="viewOptions"
:size="'md'"
class="w-full max-w-md"
>
<template #option="{ option }">
<div class="flex items-center space-x-2">
<component
:is="option.icon"
class="w-5 h-5"
/>
<span>{{ option.label }}</span>
<UIBadge
v-if="option.count"
size="sm"
variant="gray"
>
{{ option.count }}
</UIBadge>
</div>
</template>
</UISegmentedControl>
</template>
<script setup lang="ts">
import {
ListIcon,
GridIcon,
TableIcon
} from '@gohighlevel/ghl-icons/24/outline'
interface ViewOption {
value: string
label: string
icon: any
count?: number
}
const viewOptions = ref<ViewOption[]>([
{ value: 'list', label: 'List', icon: ListIcon, count: 12 },
{ value: 'grid', label: 'Grid', icon: GridIcon, count: 12 },
{ value: 'table', label: 'Table', icon: TableIcon, count: 12 }
])
const selectedView = ref('list')
</script>

Props

  • modelValue (string | number): Selected option value
  • options (array): Array of option objects
  • size (string): Component size (‘sm’ | ‘md’ | ‘lg’)
  • disabled (boolean): Disable control
  • fullWidth (boolean): Fill container width
  • name (string): Input name for forms
  • required (boolean): Mark as required
  • variant (string): Visual variant
  • rounded (boolean): Use rounded corners

Events

  • update:modelValue: Selection change
  • change: Value changed
  • focus: Control focused
  • blur: Control blurred

Slots

  • option: Custom option template
  • prefix: Content before options
  • suffix: Content after options
  • badge: Custom badge content

Usage Examples

  1. Basic Usage:
<template>
<UISegmentedControl
v-model="selectedTab"
:options="[
{ value: 'all', label: 'All' },
{ value: 'active', label: 'Active' },
{ value: 'archived', label: 'Archived' }
]"
/>
</template>
<script setup>
const selectedTab = ref('all')
</script>
  1. With Icons and Badges:
<template>
<UISegmentedControl
v-model="viewMode"
:options="viewModes"
size="lg"
class="mb-4"
>
<template #option="{ option }">
<div class="flex items-center gap-2">
<component
:is="option.icon"
:class="[
'w-5 h-5',
option.value === viewMode
? 'text-primary-600'
: 'text-gray-500'
]"
/>
<span>{{ option.label }}</span>
<UIBadge
v-if="option.count"
:variant="getBadgeVariant(option)"
size="sm"
>
{{ option.count }}
</UIBadge>
</div>
</template>
</UISegmentedControl>
<component :is="currentViewComponent" v-bind="viewProps" />
</template>
<script setup>
import {
InboxIcon,
ArchiveIcon,
TrashIcon
} from '@gohighlevel/ghl-icons/24/outline'
const viewModes = [
{
value: 'inbox',
label: 'Inbox',
icon: InboxIcon,
count: 12
},
{
value: 'archived',
label: 'Archived',
icon: ArchiveIcon,
count: 5
},
{
value: 'trash',
label: 'Trash',
icon: TrashIcon,
count: 2
}
]
const viewMode = ref('inbox')
const getBadgeVariant = (option) => {
switch (option.value) {
case 'inbox': return 'primary'
case 'archived': return 'success'
case 'trash': return 'error'
default: return 'gray'
}
}
</script>
  1. Form Integration:
<template>
<form @submit.prevent="handleSubmit">
<div class="space-y-4">
<label class="block">
<span class="text-sm font-medium">
Notification Preferences
</span>
<UISegmentedControl
v-model="formData.notifications"
:options="notificationOptions"
name="notifications"
required
class="mt-1"
/>
</label>
<label class="block">
<span class="text-sm font-medium">
Theme
</span>
<UISegmentedControl
v-model="formData.theme"
:options="themeOptions"
name="theme"
size="sm"
class="mt-1"
/>
</label>
<UIButton type="submit">
Save Preferences
</UIButton>
</div>
</form>
</template>
<script setup>
const formData = reactive({
notifications: 'all',
theme: 'light'
})
const notificationOptions = [
{ value: 'all', label: 'All' },
{ value: 'important', label: 'Important Only' },
{ value: 'none', label: 'None' }
]
const themeOptions = [
{ value: 'light', label: 'Light' },
{ value: 'dark', label: 'Dark' },
{ value: 'system', label: 'System' }
]
</script>

Best Practices

  1. Visual Design:

    • Clear active state
    • Consistent spacing
    • Appropriate sizing
    • Visual hierarchy
  2. User Experience:

    • Immediate feedback
    • Touch targets
    • Clear labels
    • Logical grouping
  3. Accessibility:

    • Keyboard navigation
    • ARIA labels
    • Focus states
    • Role attributes
  4. Responsiveness:

    • Mobile friendly
    • Flexible width
    • Touch support
    • Overflow handling

Common Use Cases

  1. View Switcher:
<template>
<div class="space-y-4">
<UISegmentedControl
v-model="displayMode"
:options="displayOptions"
class="w-full sm:w-auto"
/>
<div :class="getLayoutClass">
<ItemCard
v-for="item in items"
:key="item.id"
:item="item"
:display-mode="displayMode"
/>
</div>
</div>
</template>
<script setup>
const displayMode = ref('grid')
const displayOptions = [
{ value: 'grid', label: 'Grid View' },
{ value: 'list', label: 'List View' }
]
const getLayoutClass = computed(() => ({
'grid grid-cols-3 gap-4': displayMode.value === 'grid',
'space-y-4': displayMode.value === 'list'
}))
</script>
  1. Filter Controls:
<template>
<div class="filter-controls space-y-4">
<UISegmentedControl
v-model="filters.status"
:options="statusOptions"
@change="applyFilters"
/>
<UISegmentedControl
v-model="filters.timeRange"
:options="timeRangeOptions"
size="sm"
@change="applyFilters"
/>
<DataTable
:data="filteredData"
:loading="loading"
/>
</div>
</template>
  1. Wizard Steps:
<template>
<div class="wizard">
<UISegmentedControl
v-model="currentStep"
:options="steps"
:disabled="true"
class="mb-8"
>
<template #option="{ option }">
<div class="flex items-center space-x-2">
<div
class="w-6 h-6 rounded-full flex items-center justify-center"
:class="getStepClass(option)"
>
<span v-if="isStepComplete(option)">
<CheckIcon class="w-4 h-4" />
</span>
<span v-else>
{{ option.step }}
</span>
</div>
<span>{{ option.label }}</span>
</div>
</template>
</UISegmentedControl>
<component
:is="currentStepComponent"
v-bind="stepProps"
@complete="nextStep"
@back="previousStep"
/>
</div>
</template>