UI.MD

Segmented Control

A compact in-place switch for choosing one of a small set of options, drawn as one track of options with a sliding thumb.

Installation

$
npx shadcn@latest add https://ui.mattdowney.com/r/segmented-control.json

Usage

import { SegmentedControl } from "@/components/ui/segmented-control"

Examples

Default

A two-option value switch. Arrow keys move the selection with wrap-around, so the group always reports the option the user is on.

Sizes

The same heights a Button or Input of the same size name measures, 28 / 32 / 40, so the control sits level with the button beside it in a header or title row. Corners come from the action ramp for the same reason.

With icons

Give an option an icon to draw it before the label. iconOnly drops the labels for a toolbar-width control and keeps each one as the option's accessible name and hover title, so a bare control is never a nameless one.

Over media

tone="onMedia" swaps the token palette for fixed black and white plus a backdrop blur, because no semantic color can be trusted over a photo. Only use it on media; on a normal surface it reads as a foreign element.

API Reference

PropTypeDefaultDescription
valuestring-The selected option's value. Controlled: pair with onValueChange.
onValueChange(value: string) => void-Called with the newly selected value, from a click or an arrow key.
optionsSegmentedOption[]-The choices, as { value, label, icon? }. Order is the render order and the arrow-key order. An icon renders before the label, or alone under iconOnly.
iconOnlybooleanfalseDraw the icons alone and drop the visible labels. Each label stays as that option's accessible name and hover title. Group-level, not per option: a row of peers where some are labelled and some are not is a defect, not a layout.
size"sm" | "default" | "lg""default"Widget height on the kit's control ramp: 28 / 32 / 40px, the same as a Button of the same size name. The options sit inside a 2px inset and a 1px border.
tone"default" | "onMedia""default"default is a neutral track with an accent thumb, the same paint as Tabs; onMedia is fixed black and white with a backdrop blur, for sitting over a photo or video.
aria-labelstring-Required. The control is a radiogroup and has no visible label of its own, so it needs a name.