Segment
Overview
A linear set of two or more mutually exclusive options presented as a unified control, with a sliding indicator that follows the active option. Use it for compact segmented pickers — time ranges, view toggles, or any small set of single-choice options — where the selection should change immediately. For a set of independent two-state buttons (single or multiple), use SToggleGroup; for plain tabbed content panels, use STabs.
Usage
Features
- 🎯 Mutually exclusive single selection — one active segment at a time, backed by
useControllableState - 🎚 Controlled / uncontrolled —
v-modelanddefaultValue(with type-safe generic values viaT extends SegmentOptionData) - 🧩 Data-driven composition — pass
itemsand let AriaSegmentCompactown iteration and indicator layout - ✨ Optional sliding indicator —
enableIndicator(defaulttrue), fully customizable through theindicatorslot - ⌨️ Roving focus keyboard navigation — arrow keys move between segments,
loopwraps around, and arrow direction reverses in RTL - 🎨 6 sizes × 2 orientations × 2 shapes (square/rounded) × 2 fills (auto/full) via
segmentVariants - 🧩
uiper-slot class overrides (root/list/trigger/indicator/indicatorContent), plus item-leveldisabled - ♿
role="tab"+aria-selectedwithdata-statereflection, axe-clean
Component family
SSegment— the styled wrapper that forwards props to the Aria compact and injectssegmentVariantsclassesSegmentRoot/SegmentList/SegmentTrigger/SegmentIndicator(Aria) — domain primitives wrapping Tabs; DOM usesdata-vean-segment-*SegmentCompact(Aria) — data-driven composition of those primitives; import from@vean/aria/segmentfor unstyled usage
Demos
Basic
Icon
API
Aria Layer
6Segment<T>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | ClassValue | — | Additional class names applied to the root element. |
ui | Partial<SegmentUi> | — | Per-slot class overrides for the component. |
size | ThemeSize | — | Visual size of the component. |
shape | SegmentShape | — | Shape of the component. |
fill | SegmentFill | — | Fill. |
items | T[] | — | Items rendered by the component. |
enableIndicator | boolean | — | Whether to enable indicator. |
listProps | SegmentListProps | — | Properties forwarded to the list element. |
triggerProps | SegmentTriggerProps | — | Properties forwarded to the trigger element. |
indicatorProps | SegmentIndicatorProps | — | Properties forwarded to the indicator element. |
modelValue | T['value'] | null | — | The controlled value of the tab to activate. Can be bind as `v-model`. |
defaultValue | T['value'] | null | — | The value of the tab that should be active when initially rendered. Use when you do not need to control the state
of the tabs |
activationMode | TabsActivationMode | automatic | Whether a tab is activated automatically (on focus) or manually (on click). |
unmountOnHide | boolean | `true` | When `true`, the element will be unmounted on closed state. |
dir | Direction | — | The direction of navigation between items. |
loop | boolean | false | Whether keyboard navigation should loop around |
orientation | DataOrientation | — | The orientation of the group. Mainly so arrow navigation is done accordingly (left & right vs. up & down) |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:modelValue | Exclude<T, undefined> | Event handler called when the value changes |
Slots
| Slot Name | Parameters | Description |
|---|---|---|
item | T & { selected: boolean; } | Custom content for the item slot. |
indicator | — | Custom content for the indicator slot. |
SegmentCompact<T>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
items | T[] | — | Items rendered by the component. |
enableIndicator | boolean | — | Whether to enable indicator. |
listProps | SegmentListProps | — | Properties forwarded to the list element. |
triggerProps | SegmentTriggerProps | — | Properties forwarded to the trigger element. |
indicatorProps | SegmentIndicatorProps | — | Properties forwarded to the indicator element. |
modelValue | T['value'] | null | — | The controlled value of the tab to activate. Can be bind as `v-model`. |
defaultValue | T['value'] | null | — | The value of the tab that should be active when initially rendered. Use when you do not need to control the state
of the tabs |
activationMode | TabsActivationMode | automatic | Whether a tab is activated automatically (on focus) or manually (on click). |
unmountOnHide | boolean | `true` | When `true`, the element will be unmounted on closed state. |
dir | Direction | — | The direction of navigation between items. |
loop | boolean | false | Whether keyboard navigation should loop around |
orientation | DataOrientation | — | The orientation of the group. Mainly so arrow navigation is done accordingly (left & right vs. up & down) |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:modelValue | Exclude<T, undefined> | Event handler called when the value changes |
Slots
| Slot Name | Parameters | Description |
|---|---|---|
item | T & { selected: boolean; } | Custom content for the item slot. |
indicator | — | Custom content for the indicator slot. |
SegmentIndicator
Props
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
SegmentList
SegmentRoot<T>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
modelValue | T | — | The controlled value of the tab to activate. Can be bind as `v-model`. |
defaultValue | T | — | The value of the tab that should be active when initially rendered. Use when you do not need to control the state
of the tabs |
activationMode | TabsActivationMode | automatic | Whether a tab is activated automatically (on focus) or manually (on click). |
unmountOnHide | boolean | `true` | When `true`, the element will be unmounted on closed state. |
dir | Direction | — | The direction of navigation between items. |
loop | boolean | false | Whether keyboard navigation should loop around |
orientation | DataOrientation | — | The orientation of the group. Mainly so arrow navigation is done accordingly (left & right vs. up & down) |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:modelValue | Exclude<T, undefined> | Event handler called when the value changes |
SegmentTrigger
Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | DefinedValue | — | A unique value that associates the trigger with a content. |
disabled | boolean | — | When `true`, prevents the user from interacting with the tab. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
Notes
Architecture and benchmark differences
SegmentCompact composes the Aria SegmentRoot/SegmentList/SegmentTrigger/SegmentIndicator primitives (each wrapping the matching Tabs part): it iterates items, forwards listProps/triggerProps/indicatorProps, and exposes item/indicator slots. SegmentTrigger keeps the Tabs ARIA pattern (role="tab" + roving focus), so loop and dir (RTL) behave identically to tabs. SSegment is a thin wrapper that only computes segmentVariants (an alias of tabsVariants) and calls provideSegmentUi. The indicator is measured asynchronously (resize observer + post-flush watch), so it appears one frame after mount. segment maps to the segmented-control pattern; the benchmark libraries express it natively except shadcn, which typically uses Tabs.
| Capability | VeanUI | Ant Design | Element Plus | Mantine | Naive UI | shadcn |
|---|---|---|---|---|---|---|
| Aria/styled split | ✅ | — | — | — | — | — |
| Single (mutually exclusive) | ✅ | ✅ | ✅ | ✅ | ✅ | — |
| Controlled / uncontrolled | ✅ | ✅ | ✅ | ✅ | ✅ | — |
| Data-driven items | ✅ | ✅ | ✅ | ✅ | ✅ | — |
| Sliding indicator | ✅ | ✅ | ✅ | ✅ | ✅ | — |
| Roving focus arrow keys | ✅ | ✅ | ✅ | ✅ | — | — |
| Loop navigation | ✅ | — | — | — | — | — |
| RTL-aware arrow direction | ✅ | — | — | — | — | — |
| Orientation (vertical) | ✅ | — | — | ✅ | — | — |
| Sizes × shapes × fills | ✅ | — | — | ✅ | — | — |
| Item-level disabled | ✅ | ✅ | ✅ | ✅ | ✅ | — |
| Custom item / indicator slots | ✅ | ✅ | ✅ | ✅ | ✅ | — |
Cautions
loopandunmountOnHidedefault totrue;enableIndicatordefaults totrue. Set:enable-indicator="false"to hide the sliding indicator (active state is then reflected throughdata-state).- The indicator is positioned after an asynchronous layout measurement — it renders one frame after the active segment changes.
- Segments use the Tabs ARIA pattern (
role="tab"+aria-selected), so avoid nesting segments inside another tabs-like control. defaultValueis only read on mount; subsequent changes are ignored (usev-modelfor external control).- For icon-only items, add
aria-labelso the segment has an accessible name. unmountOnHideonly matters when content panels are attached; a bareSSegmentrenders no content area.
FAQ
What is the difference between SSegment and SToggleGroup?
SSegment is a single mutually exclusive segmented control with a sliding indicator — exactly one option is active. SToggleGroup coordinates a set of independent toggle buttons that can be single or multiple selection, with no indicator.
How do I disable the sliding indicator?
Pass :enable-indicator="false". The selected segment is then highlighted through the data-selected style rules instead.
How do I make arrow keys wrap around?
Keep loop (default true) — focus wraps from the last segment to the first and vice versa. Disabled segments are skipped automatically.
How do I control the selected value externally?
Bind v-model for two-way control, or pass only defaultValue for uncontrolled usage. Use items to declare options; each item supports value, label, and disabled.
Can I style the indicator and items?
Yes — the indicator slot replaces the default indicator content, the item slot receives { ...item, active } for full customization, and ui.root / ui.list / ui.trigger / ui.indicator override classes per slot.