Segmented Control
Segmented control allows users to select one option from a set of related choices. It features a sliding indicator animation, supports icons, two color variants (primary and neutral), and two visual styles (simple and expressive).
Segmented Control Playground
Segmented Control Playground
Preview
Code
List
Grid
Table
Compact
Controls
Type:
Primary
Neutral
Style:
Simple
Expressive
Size:
Small
Medium
Large
Display:
Icon + Text
Icon Only
Text Only
Width:
Equal
Content
Dividers:
Off
On
Installation
You can add the segmented control component to your project manually:
1
Install the following dependencies:
npm
pnpm
yarn
bun
npm install @react-aria/focus2
Copy and paste the following code into your project.
SegmentedControl.tsx
Segment.tsx
Loading source code...
3
Update the import paths to match your project setup.
Update the import aliases (e.g. @/components, @/utils) in the copied files to match your project's path configuration.
Basic Usage
import { useState } from 'react';
import { SegmentedControl } from '@versaui/ui/components/SegmentedControl';
import { List, GridFour, SquaresFour } from '@phosphor-icons/react';
const [view, setView] = useState('list');
// Basic segmented control (Simple style)
<SegmentedControl
items={[
{ id: 'day', label: 'Day' },
{ id: 'week', label: 'Week' },
{ id: 'month', label: 'Month' },
]}
selectedId={view}
onChange={setView}
/>
// Expressive style
<SegmentedControl
type="primary"
style="expressive"
items={[
{ id: 'day', label: 'Day' },
{ id: 'week', label: 'Week' },
{ id: 'month', label: 'Month' },
]}
selectedId={view}
onChange={setView}
/>
// Neutral variant with expressive style
<SegmentedControl
type="neutral"
style="expressive"
items={[
{ id: 'day', label: 'Day' },
{ id: 'week', label: 'Week' },
{ id: 'month', label: 'Month' },
]}
selectedId={view}
onChange={setView}
/>
// With icons
<SegmentedControl
items={[
{ id: 'list', label: 'List', icon: <List /> },
{ id: 'grid', label: 'Grid', icon: <GridFour /> },
{ id: 'board', label: 'Board', icon: <SquaresFour /> },
]}
selectedId={view}
onChange={setView}
/>
// Icon only mode
<SegmentedControl
iconOnly
items={[
{ id: 'list', icon: <List /> },
{ id: 'grid', icon: <GridFour /> },
{ id: 'board', icon: <SquaresFour /> },
]}
selectedId={view}
onChange={setView}
/>
// With dividers
<SegmentedControl
showDividers
items={[
{ id: 'all', label: 'All' },
{ id: 'active', label: 'Active' },
{ id: 'archived', label: 'Archived' },
]}
selectedId={status}
onChange={setStatus}
/>
// Content width mode
<SegmentedControl
widthMode="content"
items={[
{ id: 'all', label: 'All' },
{ id: 'active', label: 'Active' },
{ id: 'archived', label: 'Archived' },
]}
selectedId={status}
onChange={setStatus}
/>
// Neutral type
<SegmentedControl
type="neutral"
items={[
{ id: 'all', label: 'All' },
{ id: 'active', label: 'Active' },
{ id: 'archived', label: 'Archived' },
]}
selectedId={status}
onChange={setStatus}
/>SegmentItem Interface
interface SegmentItem {
id: string; // Unique identifier
label?: string; // Button text
icon?: ReactNode; // Optional icon
}Accessibility
- Each segment uses
role="button"witharia-pressedto indicate selection - Keyboard activation with
EnterandSpacekeys aria-disabledindicates disabled state- Focus ring visible on keyboard navigation
SegmentedControl Props
| Prop | Type | Default | Description |
|---|---|---|---|
items | SegmentItem[] | — | Array of segment items. |
selectedId | string | — | Currently selected segment ID. |
onChange | (id: string) => void | — | Selection change handler. |
type | 'primary' | 'neutral' | 'primary' | Color theme variant. |
style | 'simple' | 'expressive' | 'simple' | Visual style treatment. Simple uses clean solid surfaces; expressive uses rich gradient fills, outlines, and inset shadows. |
size | 'small' | 'medium' | 'large' | 'medium' | Button size. |
iconOnly | boolean | false | Show only icons (no labels). |
showDividers | boolean | false | Show vertical dividers between segments. |
widthMode | 'equal' | 'content' | 'equal' | Equal distributes segments evenly across available width. Content sizes segments to fit their content. |