SegmentedControl
One choice out of a few, shown side by side in a track, with the selection sliding between them.
import { SegmentedControl } from "@butteryui/react";Demo
When to use it
Use it for
- Two to five options that are all worth seeing at once: Gradient or Solid, Fit or Fill
- Switching a view where one option is always on
- A control that should span its column, as in a form card, with layout="fill"; a toolbar or a dense panel with size="sm"; a row of 48px fields or buttons with size="lg"
Not for
- More than five options or long labels: use Select
- Options that can all be off, or several on: use toggle buttons
- Navigation between pages: use tabs or links
Examples
Aspect picker
A deck's slide shape, with labels that differ from the values the control reports.
Every slide is 4:3
Export quality
A disabled option and a disabled control: WebP isn't on this plan, and quality only applies to JPG.
WebP comes with the Team plan.
PNG keeps every pixel, so quality doesn't apply
Fill switch
Gradient or solid fill for a slide background, kept in state so the swatches follow it and Reset can set it back.
Showing gradient swatches.
Sizes and fill
A small control in a header beside a full-width one that spans its form with layout="fill".
A 12 oz bag every 2 weeks
View switch
A deck's slides with an icon-only view switch in its toolbar, each icon's label in its tooltip and name.
Quarterly review
Showing 4 slides as a list
- Title slide
- Agenda
- Q3 in numbers
- Thank you
Variants and states
Two options
Click a segment or use the arrow keys: the selection slides, and one is always chosen.
Showing gradient swatches.
Equal widths, a disabled option
Sizes
md, the default, is 34px tall (30 compact); sm is 28 for a toolbar or a dense panel; lg is 48 (40 compact), its segments 4px inside the track, beside 48px buttons and fields.
Hug and fill
hug, the default, keeps the track as wide as its segments, even in a Stack that stretches its children; fill spans the width it's given, its segments sharing it equally.
Props
SegmentedControl
| Prop | Type | Default | Description |
|---|---|---|---|
optionsrequired | readonly (Value | SegmentedControlOption<Value>)[] | The choices, as values that are also their labels or as { value, label, disabled }. | |
value | Value | The chosen value. Pair with onValueChange to control it. | |
defaultValue | Value | The value chosen at first when uncontrolled. Defaults to the first option. | |
onValueChange | ((value: Value) => void) | Called with the new value when the user picks another segment. | |
labelrequired | string | What the choice is about, for screen readers, such as "Picture fit". | |
disabled | boolean | Keeps every segment from being chosen. | |
iconOnly | boolean | Shows each option's icon in place of its label, which becomes its tooltip and name. Give every option an icon. | |
size | "sm" | "md" | "lg" | "md" | The track's height: "md" (34px, 30 compact, the default), "sm" (28px) for a toolbar or a dense panel, or
"lg" (48px, 40 compact) beside 48px buttons and fields. |
layout | "hug" | "fill" | "hug" | "hug" (the default) keeps the control as wide as its segments wherever it's placed, even in a Stack or a
grid cell that stretches its children; "fill" spans the width it's given, its segments sharing it equally. |
It also takes Omit<ComponentPropsWithRef<"div">, "defaultValue" | "onChange" | "dir">.
SegmentedControlOption
One option of a `SegmentedControl`.
| Field | Type | Default | Description |
|---|---|---|---|
valuerequired | Value | What value and onValueChange carry. | |
labelrequired | string | What the segment says. | |
disabled | boolean | Keeps the segment from being chosen. | |
icon | ComponentType<IconComponentProps> | An icon before the label, or in its place with iconOnly. |
Accessibility
A radio group named by label: Tab reaches the chosen segment, arrow keys move the choice and pick as they go, and one segment is always chosen.
| Keys | What they do |
|---|---|
| Tab | Reaches the chosen segment, the control's one Tab stop |
| Left or Right arrow | Moves the choice to the segment before or after, going round at the ends and skipping disabled ones |
| Home or End | Moves focus to the first or last segment, which Space then chooses |
| Space | Chooses the segment with focus |
Motion
Its selection indicator slides to the chosen option on `spring.snappy`, inside one track.
With reduced motion: The selection appears on the chosen segment at once instead of sliding.
Layout
A pill track 34px tall (--bt-h-control, 30 compact) at size="md", 28px (--bt-h-sm) at "sm" or 48px (--bt-h-cta, 40 compact) at "lg", padded 2px (4px at "lg") around segments that share one width, the widest label's. With layout="hug", the default, it's as wide as that makes it wherever it's placed, a Stack or a grid cell that stretches its children included; with layout="fill" it spans the width it's given, its segments sharing it equally, and in an Inline it takes the room its siblings leave. It's never wider than its container, where a label that doesn't fit truncates with an ellipsis and shows in full as its title. Each segment's full height is its hit area.
Related
See it in the roastery demo's orders. See it in the Halfmoon shop demo. See it in the Deckhand demo.