Skip to main content
Clever Ops

Segmented Button Group

Segmented control drawn like an instrument panel, one hairline frame, a sprung layoutId thumb under the active segment, an indicator lamp, roving tabindex with arrow keys, and cva sizes.

Preview & code

Loading Segmented Button Group preview

Installation

pnpm dlx shadcn@latest add @cleverui/button-group-segmented

Requires the @cleverui registry in your components.json, a one-off, two-line setup.

Usage

tsx
import { ButtonGroupSegmented } from '@/components/ui/button-group-segmented'
Exampletsx
'use client'

import { useState } from 'react'
import { ButtonGroupSegmented } from '@/components/ui/button-group-segmented'

export default function ButtonGroupSegmentedDemo() {
  const [density, setDensity] = useState('comfortable')
  const [range, setRange] = useState('30d')

  return (
    <div className="mx-auto flex min-h-[360px] max-w-xl flex-col items-center justify-center gap-12 px-6 py-16">
      <div className="flex w-full flex-col items-center gap-3">
        <p className="font-mono text-[11px] uppercase tracking-[0.2em] text-muted-foreground">
          Ledger density
        </p>
        <ButtonGroupSegmented
          label="Ledger density"
          value={density}
          onValueChange={setDensity}
          options={[
            { value: 'compact', label: 'Compact' },
            { value: 'comfortable', label: 'Comfortable' },
            { value: 'spacious', label: 'Spacious' },
          ]}
        />
        <p className="text-sm text-muted-foreground">
          Showing{' '}
          <span className="font-medium text-foreground">
            {density === 'compact' ? '32' : density === 'comfortable' ? '18' : '10'}
          </span>{' '}
          rows per page
        </p>
      </div>

      <div className="flex w-full flex-col items-center gap-3">
        <p className="font-mono text-[11px] uppercase tracking-[0.2em] text-muted-foreground">
          Reporting window
        </p>
        <ButtonGroupSegmented
          label="Reporting window"
          size="sm"
          value={range}
          onValueChange={setRange}
          options={[
            { value: '7d', label: '7d' },
            { value: '30d', label: '30d' },
            { value: '90d', label: '90d' },
            { value: 'ytd', label: 'YTD' },
            { value: 'custom', label: 'Custom', disabled: true },
          ]}
        />
        <p className="text-sm text-muted-foreground">
          {range === '7d' && 'Week of Jun 30 – Jul 6 · 1,248 events'}
          {range === '30d' && 'Jun 7 – Jul 6 · 5,914 events'}
          {range === '90d' && 'Apr 8 – Jul 6 · 17,302 events'}
          {range === 'ytd' && 'Jan 1 – Jul 6 · 41,088 events'}
        </p>
      </div>

      <div className="flex w-full flex-col items-center gap-3">
        <p className="font-mono text-[11px] uppercase tracking-[0.2em] text-muted-foreground">
          Editor mode
        </p>
        <ButtonGroupSegmented
          label="Editor mode"
          size="lg"
          defaultValue="design"
          options={[
            { value: 'design', label: 'Design' },
            { value: 'prototype', label: 'Prototype' },
            { value: 'handoff', label: 'Handoff' },
          ]}
        />
      </div>
    </div>
  )
}

API reference

ButtonGroupSegmented accepts the following props.

PropTypeDefaultDescription
options*ButtonGroupSegmentedOption[]2–5 segments; disabled segments render at opacity 50 and are skipped by arrow navigation
valuestringcontrolled selected value; pair with onValueChange
defaultValuestringuncontrolled initial selection; falls back to the first enabled option
onValueChange(value: string) => voidfires when a segment is selected by click or Enter/Space
sizeVariantProps<typeof segmentButtonVariants>['size']'md'cva sizes, h-8 text-xs / h-9 text-sm / h-11 text-sm segment heights
labelstring'View options'accessible name for the group

…plus everything from Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange' | 'defaultValue'>: className, event handlers, aria attributes and the rest pass straight through.

Referenced types

Typestsx
export interface ButtonGroupSegmentedOption {
  value: string
  label: string
  disabled?: boolean
}

Frequently asked questions

How do I install the Segmented Button Group component?

Register the @cleverui registry in your components.json ("@cleverui": "https://cleverops.com.au/r/{name}.json"), then run `npx shadcn@latest add @cleverui/button-group-segmented`. The shadcn CLI copies the source into your project, you own the code from that point. You can also copy the source directly from this page.

What dependencies does Segmented Button Group require?

Segmented Button Group uses motion and class-variance-authority on top of React and Tailwind CSS. You don't need to install them manually, the shadcn CLI resolves and installs npm dependencies automatically when you add the component.

Can I theme Segmented Button Group to match my brand?

Yes. The component is styled entirely with Tailwind utilities over standard design tokens (primary, background, muted, border), so it inherits your existing shadcn/ui theme automatically. Change your CSS variables and the component follows, no source edits needed for palette, radius, or fonts.

Want this built for you?

CleverUI is built by the Clever Ops web team. If you'd rather ship a hand-coded, fast, custom website than assemble one, we build them for Australian businesses every week.

Explore web development services