Segmented
segmentedSelects one option from a compact segmented control with a moving indicator.
Usage
Basic usage
Pass in the items array, defaultValue sets the initial selection segment, and the slider transitions smoothly.
<Segmented
items={[
{ value: "day", label: "Day" },
{ value: "week", label: "week" },
{ value: "month", label: "month" },
]}
defaultValue="week"
aria-label="Period"
/>Icon segment
label ariaLabel Provides accessible names when using icons.
<Segmented
items={[
{ value: "grid", ariaLabel: "Grid View", label: <LayoutGrid className="size-4" /> },
{ value: "list", ariaLabel: "List view", label: <List className="size-4" /> },
{ value: "map", ariaLabel: "Map View", label: <Map className="size-4" /> },
]}
defaultValue="grid"
aria-label="View"
/>Logo within paragraph
label can be placed in rich nodes, such as embedding a discount Tag in the billing cycle.
<Segmented
items={[
{ value: "monthly", label: "Monthly payment" },
{
value: "yearly",
ariaLabel: "Pay annually and save 2 months",
label: (
<>
Paid annually
<Tag variant="soft" tone="success" size="sm">Save 2 months</Tag>
</>
),
},
]}
defaultValue="monthly"
aria-label="Billing cycle"
/>Semantic tone
tone takes the same name and the same values as on TabsList: the label of the selected segment picks up a semantic colour while the slider stays a white pill. The neutral default keeps the existing neutral selected state, so passing nothing changes nothing.
<Segmented
items={[
{ value: "day", label: "Day" },
{ value: "week", label: "week" },
{ value: "month", label: "month" },
]}
defaultValue="week"
tone="brand"
aria-label="Period"
/>Size
size="sm" is used for compact scenes such as toolbars.
<>
<Segmented size="sm" items={periodItems} defaultValue="day" aria-label="Period-Small" />
<Segmented items={periodItems} defaultValue="day" aria-label="Cycle-Medium" />
</>Disabled
Single segment disabled skips this item; overall disabled disables all.
<>
<Segmented
items={[
{ value: "a", label: "A" },
{ value: "b", label: "B", disabled: true },
{ value: "c", label: "C" },
]}
defaultValue="a"
aria-label="Example"
/>
<Segmented items={periodItems} defaultValue="week" disabled aria-label="Period" />
</>When to use
Use Segmented for two to five mutually exclusive horizontal choices, such as Day/Week/Month, Grid/List/Map, or Monthly/Annual. An indicator highlights the active item, and the selected value is a single string. Use Radio for vertically arranged form choices, Tabs for switching page-level panels, or Select for a larger collapsed set.
Import
import { Segmented } from "@hulianui/ui"Props
Segmented
| Name | Type | Default | Description |
|---|---|---|---|
| items * | SegmentedItem[] | - | Definitions for the available segments. |
| value | string | - | Selected value in controlled mode. |
| defaultValue | string | First non-disabled segment | Initial selected value in uncontrolled mode. |
| disabled | boolean | false | Whether to disable the entire control. |
| size | "sm"|"md" | "md" | Visual size. |
| tone | "brand"|"success"|"warning"|"danger"|"neutral" | "neutral" | Semantic colour of the selected segment. Only the label is coloured; the slider stays a white pill. The neutral default keeps the existing neutral selected state (text-foreground) byte for byte rather than turning the brand colour grey, and the values match tone on TabsList in Tabs. |
| className | string | - | Additional class name for the root element. |
| aria-label | string | - | Accessible label for the control when no visible title is present. |
SegmentedItem
| Name | Type | Default | Description |
|---|---|---|---|
| value * | string | - | Unique value that identifies the segment and its selected state. |
| ariaLabel | string | - | Accessible label for non-text content such as an icon or logo; otherwise screen readers fall back to value. |
| disabled | boolean | false | Whether to disable this segment. |
Events
Segmented
| Event | Type | Description |
|---|---|---|
| onValueChange | (value: string) => void | Called with the newly selected value; selection is mutually exclusive. |
Slots
SegmentedItem
| Slot | Type | Description |
|---|---|---|
| label * | ReactNode | Segment content, such as text or an icon. |
Example
<Segmented
items={[
{ value: "day", label: "Day" },
{ value: "week", label: "Week" },
{ value: "month", label: "Month" },
]}
defaultValue="week"
aria-label="Period"
/>Icon-only segments, each with its own ariaLabel:
<Segmented
items={[
{ value: "grid", ariaLabel: "Grid view", label: <LayoutGrid className="size-4" /> },
{ value: "list", ariaLabel: "List view", label: <List className="size-4" /> },
]}
defaultValue="grid"
aria-label="View"
/>Usage guidelines
- When space runs out, segments shrink and truncate; they never disappear. Each segment is
min-w-0 flex-1 truncate, so every option stays visible and clickable at any width. Before 0.27.0 the missingmin-w-0combined withwhitespace-nowrapmade segments incompressible, the root grew to the sum of all segment labels, and the overflow was clipped by the parent, leaving options that exist but cannot be reached (#114). - Compressible is not the same as usable: four CJK segments in 150px are all ellipsis. In narrow containers use Select, or degrade by container width the way InspectorPanel does.
- When
labelis an icon, logo, or other non-text node, provideariaLabel; otherwise screen readers fall back to the rawvalue. - Controlled usage requires
valueandonValueChange. For uncontrolled state, provide onlydefaultValue. - Segmented is single-select only. Use ToggleGroup when several items may remain active.
Related
Input · Textarea · Select · Checkbox · CheckboxGroup · Radio
Playground
<Segmented
items={[{ value: "day", label: "Day" }, { value: "week", label: "Week" }, { value: "month", label: "month" }]}
defaultValue="week"
/>