Segment

free

Segment switches between a few related options from one compact control.

Preview
Dark
import Segment from '@/components/ui/Segment';

export default function UsageDemo() {
  return (
    <Segment defaultValue="board">
      <Segment.Item value="list">List</Segment.Item>
      <Segment.Item value="board">Board</Segment.Item>
      <Segment.Item value="calendar">Calendar</Segment.Item>
    </Segment>
  );
}

Installation

Add this component with the NateUI CLI.

npx nateui@latest add Segment

Examples

Basic

Preview
Dark
import Segment from '@/components/ui/Segment';

export default function BasicDemo() {
  return (
    <Segment>
      <Segment.Item value="left">Left</Segment.Item>
      <Segment.Item value="center">Center</Segment.Item>
      <Segment.Item value="right">Right</Segment.Item>
    </Segment>
  );
}

Size

Preview
Dark
import Segment from '@/components/ui/Segment';

export default function SizeDemo() {
  return (
    <div className="flex flex-col items-start gap-8">
      <Segment size="sm" defaultValue="center">
        <Segment.Item value="left">Left</Segment.Item>
        <Segment.Item value="center">Center</Segment.Item>
        <Segment.Item value="right">Right</Segment.Item>
      </Segment>
      <Segment size="md" defaultValue="center">
        <Segment.Item value="left">Left</Segment.Item>
        <Segment.Item value="center">Center</Segment.Item>
        <Segment.Item value="right">Right</Segment.Item>
      </Segment>
      <Segment size="lg" defaultValue="center">
        <Segment.Item value="left">Left</Segment.Item>
        <Segment.Item value="center">Center</Segment.Item>
        <Segment.Item value="right">Right</Segment.Item>
      </Segment>
    </div>
  );
}

Multiple Selection

Preview
Dark
import Segment from '@/components/ui/Segment';

export default function MultipleDemo() {
  return (
    <Segment<'multiple'>
      selectionType="multiple"
      defaultValue={['bold', 'underline']}
    >
      <Segment.Item value="bold">Bold</Segment.Item>
      <Segment.Item value="italic">Italic</Segment.Item>
      <Segment.Item value="underline">Underline</Segment.Item>
    </Segment>
  );
}

With Icons

Preview
Dark
import Segment from '@/components/ui/Segment';
import {
    PiTextAlignCenter,
    PiTextAlignLeft,
    PiTextAlignRight,
} from 'react-icons/pi'

export default function WithIconsDemo() {
  return (
    <Segment defaultValue="left">
      <Segment.Item value="left" className="gap-2">
        <PiTextAlignLeft />
        <span>Left</span>
      </Segment.Item>
      <Segment.Item value="center" className="gap-2">
        <PiTextAlignCenter />
        <span>Center</span>
      </Segment.Item>
      <Segment.Item value="right" className="gap-2">
        <PiTextAlignRight />
        <span>Right</span>
      </Segment.Item>
    </Segment>
  );
}

Disabled

Preview
Dark
import Segment from '@/components/ui/Segment';

export default function DisabledDemo() {
  return (
    <Segment defaultValue="left">
      <Segment.Item value="left">Left</Segment.Item>
      <Segment.Item value="center" disabled>
        Center
      </Segment.Item>
      <Segment.Item value="right">Right</Segment.Item>
    </Segment>
  );
}

Controlled

Preview
Dark
Active view: board
import { useState } from 'react';
import Segment from '@/components/ui/Segment';

export default function ControlledDemo() {
  const [view, setView] = useState('board');

  return (
    <div className="flex flex-col items-start gap-4">
      <Segment value={view} onChange={(value) => setView(value)}>
        <Segment.Item value="list">List</Segment.Item>
        <Segment.Item value="board">Board</Segment.Item>
        <Segment.Item value="calendar">Calendar</Segment.Item>
      </Segment>
      <div className="text-sm text-muted-foreground">
        Active view:{' '}
        <span className="font-semibold text-foreground">
          {view || 'none'}
        </span>
      </div>
    </div>
  );
}

API

Segment is a compound control: the Segment root holds selection state and renders the sliding indicator, while each Segment.Item is one selectable option. In single mode selecting the active item again clears the selection; in multiple mode each item toggles independently.

Segment

PropDescriptionTypeDefault
selectionTypeWhether one item or several items can be active at once.'single' | 'multiple''single'
valueActive value in controlled mode. A string for single selection, an array for multiple.SegmentValue-
defaultValueInitial value when uncontrolled. A string for single selection, an array for multiple.SegmentValue-
onChangeFires with the new value whenever the selection changes.(value: SegmentValue) => void-
sizeControl size applied to every item, unless an item sets its own.ControlSize'md'
classNameClass names for the root element.string-
refRef forwarded to the root element.Ref<HTMLDivElement>-
...nativePropsNative props for the root div (except onChange).Omit<ComponentProps<'div'>, 'onChange'>-

Segment.Item

PropDescriptionTypeDefault
valueUnique value that identifies this item within the group.string-
childrenItem content, or a render function that receives the item state for a custom look.ReactNode | ((params: SegmentItemRenderProps) => ReactNode)-
disabledPrevent this item from being selected and dim it.booleanfalse
sizeControl size for this item, overriding the group size.ControlSize-
classNameClass names for the item button.string-
refRef forwarded to the item button.Ref<HTMLButtonElement>-
...nativePropsNative button props (except children).Omit<ComponentPropsWithRef<'button'>, 'children'>-

Types

type ControlSize = 'sm' | 'md' | 'lg'
 
// string for single selection, string[] for multiple selection
type SegmentValue = string | string[]
 
type SegmentItemRenderProps = {
  active: boolean
  disabled: boolean
  value: string
  onSegmentItemClick: () => void
}