Skip to content

SegmentedControl

Two to five mutually exclusive views or modes in one compact bar. Hidden native radios, so it works without JavaScript.

FormsServer-rendered

Examples

Links (URL-driven)

When every option has an href it renders a <nav> of links; the `value` option gets aria-current="page".

Example
<SegmentedControl
  label="Harness"
  value={harness}
  options={harnesses.map((h) => ({ value: h.id, label: h.label, href: routes.dashboard.connect.href(undefined, { harness: h.id }) }))}
/>

Text and icons

Example
<SegmentedControl
  name="transport"
  label="Transport"
  value="http"
  options={[{ value: 'http', label: 'HTTP' }, { value: 'sse', label: 'SSE' }]}
/>
<SegmentedControl
  name="format"
  label="Config format"
  size="sm"
  options={[
    { value: 'json', label: 'JSON', icon: 'settings' },
    { value: 'cli', label: 'CLI', icon: 'terminal' },
  ]}
/>
<SegmentedControl
  name="view"
  label="View"
  value="list"
  options={[
    { value: 'list', label: 'List', icon: 'menu', iconOnly: true },
    { value: 'grid', label: 'Grid', icon: 'layers', iconOnly: true },
  ]}
/>

Block

Fills the container, segments share the width.

Example
<SegmentedControl
  name="auth"
  label="Authentication"
  block
  value="bearer"
  options={[
    { value: 'oauth', label: 'OAuth' },
    { value: 'bearer', label: 'Bearer token' },
    { value: 'none', label: 'None' },
  ]}
/>

Usage

Usage
import { SegmentedControl } from '@switchboard/ui'

<SegmentedControl name="transport" label="Transport" value={server.transport} options={transports} />

Props

Every component also takes mix, for css() and mixins applied after its own style.

SegmentedControl

SegmentedControl props
PropTypeDefaultDescription
name*string–Radio name; posts the checked value. Must be unique on the page.
label*string–Accessible group name (aria-label). There is no visible legend.
options*SegmentOption[]–{ value, label, icon?, iconOnly?, disabled? }. iconOnly keeps label as the name and tooltip.
valuestring–Checked value. Defaults to the first option.
size'sm' | 'md''md'Height 28 or 32px.
blockboolean–Stretch to the container width.
disabledboolean–Disables every segment.

Also accepts every <div> attribute.

Accessibility

  • Each segment is a visually hidden native radio inside a <label>: one tab stop, arrow keys change the value, posts without JavaScript.
  • The group has role="radiogroup" and aria-label from label; icon-only segments are named by their label.
  • The checked segment is raised and its text darkens, so the state is not shown by colour alone.

Guidance

Do

  • Use for switching a view or a mode with two to five short options.
  • Keep labels to one or two words.

Don’t

  • Use it for navigation between pages; use LinkTabs.
  • Mix icon-only and text segments in one control.