Segmented control
Switch between a small set of related views.
controlchoice
Preview
Segmented control preview
Live · keyboard ready
When to use
- Two to five short view options like Day, Week, and Month.
- Toolbar toggles between layouts or modes that apply immediately.
When not to use
- Use tabs when each option swaps a panel of content.
- Use radio-group in forms or when options need descriptions.
- Use switch for a single on and off setting.
shadcn CLI
Also installs (1)
- manicat-foundation
Manual install(+)
registry.jsonjson
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "segmented-control",
"type": "registry:ui",
"title": "Segmented control",
"description": "Switch between a small set of related views.",
"categories": [
"inputs"
],
"dependencies": [
"motion"
],
"registryDependencies": [
"https://manicat.viraza.net/r/manicat-foundation.json"
],
"files": [
{
"path": "registry/components/segmented-control/segmented-control.tsx",
"type": "registry:component",
"target": "@components/manicat/segmented-control/segmented-control.tsx"
},
{
"path": "registry/components/segmented-control/segmented-control.module.css",
"type": "registry:component",
"target": "@components/manicat/segmented-control/segmented-control.module.css"
}
],
"docs": "Docs and live preview: https://uiarc.dev/components/segmented-control",
"meta": {
"tier": "free",
"kind": "component",
"docs": "https://manicat.viraza.net/components/segmented-control",
"markdown": "https://uiarc.dev/components/segmented-control/markdown",
"tags": [
"control",
"choice"
]
}
}Usage
Install the component, then copy this into your app. Change the content and the props to fit your screen.
example.tsxtsx
"use client";
import { useState } from "react";
import SegmentedControl from "@/components/manicat/segmented-control/segmented-control";
export function SegmentedControlExample() {
const [value, setValue] = useState("grid");
return (
<SegmentedControl
label="Layout"
value={value}
onValueChange={setValue}
options={[
{ value: "grid", label: "Grid" },
{ value: "list", label: "List" },
{ value: "map", label: "Map" },
]}
/>
);
}
Examples
Real screens rather than isolated props. Switch to Code to copy a whole example into your project.
View switcher Switch a project list between layouts; the choice drives what renders below.
View switcher
Switch a project list between layouts; the choice drives what renders below. preview
Live · keyboard ready
Showing the board view
view-switcher-switch-a-project-list-between-layouts-the-choice-drives-what-renders-below-.tsxtsx
"use client";
import { useState } from "react";
import SegmentedControl from "@/components/manicat/segmented-control/segmented-control";
export function SegmentedControlViewExample() {
const [view, setView] = useState("board");
return (
<div className="grid w-full max-w-sm gap-4">
<SegmentedControl
label="Layout"
value={view}
onValueChange={setView}
options={[
{ value: "list", label: "List" },
{ value: "board", label: "Board" },
{ value: "calendar", label: "Calendar" },
]}
/>
<div className="rounded-[var(--radius-control)] border border-dashed border-(color:--border) p-6 text-center text-sm text-(color:--text-muted)">
Showing the {view} view
</div>
</div>
);
}
Keyboard interactions
| Keys | Action |
|---|---|
| Tab | Moves focus into the control, onto the selected segment, and out again. |
| Arrow keys | Select and focus the previous or next segment, wrapping at the ends. |
| Home / End | Select the first or last segment. |
Accessibility
- Renders role="group" labelled by label, with native buttons using aria-pressed for the selected segment. Only the selected segment is a tab stop.
- The sliding pill is aria-hidden.
Motion
- A shared layoutId pill slides to the selected segment on the morph spring, scoped per instance by a LayoutGroup.
- Reduced motion moves the pill instantly and disables button transitions.
Responsive behavior
- The row is inline with max-width 100% and scrolls horizontally with a hidden scrollbar when segments do not fit.
- Segments never shrink or wrap, so keep labels short on mobile.
Performance
- One shared layoutId pill per instance, scoped by a LayoutGroup; no observers.
Notes for AI
- Use for two to five short, mutually exclusive view options like time ranges or layouts. Use tabs when each option swaps a panel, and radio-group in forms.
- Always controlled. Import it as a default export.