Segmented Morph Toggle
A segmented control whose selected block flows and morphs to the new option on a spring.
A segmented control component: the selected block flows between options on a spring using a layoutId shared element, stretching slightly along the direction of travel and then springing back, so it glides into place like a liquid. It supports controlled and uncontrolled modes, three sizes, icons and disabled options, and comes with full keyboard operation built in.
Loading preview…
npx shadcn@latest add https://webberui.com/r/segmented-morph-toggle.jsonPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<SegmentedMorphToggle />
Installation
npx shadcn@latest add https://webberui.com/r/segmented-morph-toggle.jsonOr, once registries are configured in components.json, install it as @webberui/segmented-morph-toggle.
Usage
import { SegmentedMorphToggle } from "@/components/ui/segmented-morph-toggle";
const [period, setPeriod] = React.useState("monthly");
<SegmentedMorphToggle
ariaLabel="Billing period"
value={period}
onChange={setPeriod}
options={[
{ value: "monthly", label: "Monthly" },
{ value: "quarterly", label: "Quarterly" },
{ value: "yearly", label: "Yearly" },
]}
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
options | SegmentedMorphOption[] | — | The option list; each entry has a unique value and a label, with optional icon and disabled |
value | string | — | The current value in controlled mode; once provided, it is fully controlled from outside |
defaultValue | string | the first option | Initial value in uncontrolled mode |
onChange | (value: string) => void | — | Callback when the selected value changes |
size | "sm" | "md" | "lg" | "md" | Size (padding and font size) |
ariaLabel | string | — | Group label for assistive technology |
className | string | — | Forwarded to the outermost container |
Accessibility
- Uses
role="radiogroup"androle="radio", with the selected state marked byaria-checked - Roving tabindex: Tab only lands on the selected item,
←/→/↑/↓cycle within the group, andHome/Endjump to the first and last - Disabled options are marked
disabled, skipped automatically by keyboard navigation, and not clickable with the mouse either - When the user has "reduce motion" enabled at the system level, the block jumps straight to the new position without playing the liquid stretch animation
- Name the group with
ariaLabelso screen readers announce what the toggle is for