Skip to content
kerfjsGitHub

Segmented

A row of two to five short options where exactly one is on.

SourcePackage @kerf-js/veneerTry it

Preview

tsx
import { state } from '@kerf-js/core'import { Segmented } from '@kerf-js/veneer'export function Basic() {  const v = state('week')  return (    <Segmented      label="Period"      value={v.value}      options={PERIODS}      onChange={(next) => (v.value = next)}    />  )}const PERIODS = [  { value: 'day', label: 'Day' },  { value: 'week', label: 'Week' },  { value: 'month', label: 'Month' },]

Installation

npm install @kerf-js/veneer

Try it

Change the controls: the preview and its code update together. The address keeps what you set, so you can link to it.

Live preview
Controls
Size
Codeupdates as you change the controls
import { Segmented } from '@kerf-js/veneer'<Segmented value={v.value} options={PERIODS} onChange={next => (v.value = next)} label="Period" />

Usage

tsx
import { Segmented } from '@kerf-js/veneer'

Sizes

Use size to pick sm or md.

tsx
import { state } from '@kerf-js/core'import { Segmented } from '@kerf-js/veneer'export function Sizes() {  const v = state('week')  return (    <>      <Segmented        label="Period"        size="sm"        value={v.value}        options={PERIODS}        onChange={(next) => (v.value = next)}      />      <Segmented        label="Period"        value={v.value}        options={PERIODS}        onChange={(next) => (v.value = next)}      />    </>  )}const PERIODS = [  { value: 'day', label: 'Day' },  { value: 'week', label: 'Week' },  { value: 'month', label: 'Month' },]

Icons

Give an option an icon to draw it beside its label.

tsx
import { state } from '@kerf-js/core'import { Segmented } from '@kerf-js/veneer'import type { SegmentedOption } from '@kerf-js/veneer'export function Icons() {  const v = state('list')  return (    <Segmented      label="View"      value={v.value}      options={VIEWS}      onChange={(next) => (v.value = next)}    />  )}const VIEWS: SegmentedOption<'list' | 'grid'>[] = [  { value: 'list', label: 'List', icon: 'list' },  { value: 'grid', label: 'Grid', icon: 'grid' },]

A disabled option

Mark an option disabled to show it without letting it be picked.

tsx
import { state } from '@kerf-js/core'import { Segmented } from '@kerf-js/veneer'export function DisabledOption() {  const v = state('all')  return (    <Segmented      label="Logs"      value={v.value}      options={LOGS}      onChange={(next) => (v.value = next)}    />  )}const LOGS = [  { value: 'all', label: 'All' },  { value: 'info', label: 'Info' },  { value: 'trace', label: 'Trace', disabled: true },]

Filter with counts

An option's count shows how many match.

tsx
import { derived, state } from '@kerf-js/core'import {  Row,  Segmented,  Stack,  Text} from '@kerf-js/veneer'export function Deploys() {  const show = state('all')  const rows = derived(() =>    DEPLOYS.filter(      (d) =>        show.value === 'all' || d.status === show.value,    ).slice(0, 4),  )  return (    <Stack gap="group">      <Segmented        label="Show deploys"        value={show.value}        options={DEPLOY_FILTERS}        onChange={(next) => (show.value = next)}      />      <Stack gap="tight">        {rows.value.map((d) => (          <Row            key={d.id}            justify="between"            gap="group"          >            <Text>{d.project}</Text>            <Text subtle>{d.commit}</Text>          </Row>        ))}      </Stack>    </Stack>  )}const DEPLOYS = [  {    id: 'd-4821',    project: 'storefront',    branch: 'main',    commit: '9f3ac21',    message: 'Cache product images at the edge',    author: 'Priya Raman',    status: 'Live',    minutes: 2,    at: '2026-10-31T16:42',  },  {    id: 'd-4820',    project: 'search-api',    branch: 'ranking-v2',    commit: '1be07d4',    message: 'Rank exact matches first',    author: 'Lena Fischer',    status: 'Building',    minutes: 6,    at: '2026-10-31T16:10',  },  {    id: 'd-4819',    project: 'mobile-bff',    branch: 'main',    commit: 'c44e190',    message: 'Retry timed out upstream calls',    author: 'Sam Okafor',    status: 'Failed',    minutes: 4,    at: '2026-10-31T15:27',  },  {    id: 'd-4818',    project: 'checkout',    branch: 'main',    commit: '70d2fa8',    message: 'Add Apple Pay to the last step',    author: 'Tomás Oliveira',    status: 'Live',    minutes: 3,    at: '2026-10-31T14:03',  },  {    id: 'd-4817',    project: 'edge-cache',    branch: 'main',    commit: 'e81b6c2',    message: 'Lower the stale window to 30s',    author: 'Jun Tanaka',    status: 'Live',    minutes: 1,    at: '2026-10-31T11:55',  },  {    id: 'd-4816',    project: 'billing',    branch: 'main',    commit: '2ad9e03',    message: 'Prorate seats on plan change',    author: 'Mara Lindqvist',    status: 'Live',    minutes: 3,    at: '2026-10-29T17:20',  },]const countOf = (status: string) =>  DEPLOYS.filter((d) => d.status === status).lengthconst DEPLOY_FILTERS = [  { value: 'all', label: 'All', count: DEPLOYS.length },  {    value: 'Live',    label: 'Live',    count: countOf('Live'),  },  {    value: 'Failed',    label: 'Failed',    count: countOf('Failed'),  },]

When to use

Use it for

  • Switching a view or a period
  • Options of a word or two

Reach for something else

  • Tabs, when each option has its own panel of content
  • RadioGroup, when the options need a sentence each
  • Chip, when several can be on

Accessibility

←→
Moves to the next option and picks it
Tab
Moves in and out
  • Native radios in a radiogroup, so label names the group even though it isn't drawn.

API Reference

PropTypeDefaultDescription
labelstring—The group's accessible name. Not drawn.
valueV—The value that is on.
optionsSegmentedOption<V>[]—The options: { value, label, icon, count, disabled }.
size"sm" | "md"—sm or md.
namestring—The form field name. Made up for you when left out.
fillbooleanfalseSpan the container, each option an equal share.
EventTypeDefaultDescription
onChange(v: V) => void—Called with the new value.

Parts

Every element carries a stable name, so you can style it from your own CSS: [data-scope="segmented"] [data-part="indicator"]

indicatorsweepitemcount

RelatedTabsRadio groupToggleChip