Primitives
Toggle Group
Segmented control: a strong-bordered bar of Toggles divided by hairline keylines, each filling signal when active. `type="multiple"` for independent toggles, `type="single"` for a one-of-many selector.
● LIVE
import { BoldIcon, ItalicIcon, UnderlineIcon } from "lucide-react";import { ToggleGroup, ToggleGroupItem } from "@/registry/okibi/ui/toggle-group";export default function ToggleGroupDemo() { return ( <ToggleGroup type="multiple" defaultValue={["bold"]}> <ToggleGroupItem value="bold" aria-label="Bold"> <BoldIcon /> </ToggleGroupItem> <ToggleGroupItem value="italic" aria-label="Italic"> <ItalicIcon /> </ToggleGroupItem> <ToggleGroupItem value="underline" aria-label="Underline"> <UnderlineIcon /> </ToggleGroupItem> </ToggleGroup> );}npx shadcn@latest add https://okibi.cndr.dev/r/toggle-group.jsonInstallation
Install the theme first, then add the component:
npx shadcn@latest add https://okibi.cndr.dev/r/toggle-group.jsonInstall the required dependencies:
npm install @radix-ui/react-toggle-group class-variance-authorityAdd the okibi building blocks it composes: toggle.
Copy the source from the Code tab above into components/ui/toggle-group.tsx. It uses the cn helper and the okibi theme tokens — install the theme first if you haven't.
Usage
import { BoldIcon, ItalicIcon, UnderlineIcon } from "lucide-react"
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
<ToggleGroup type="multiple" defaultValue={["bold"]}>
<ToggleGroupItem value="bold" aria-label="Bold">
<BoldIcon />
</ToggleGroupItem>
<ToggleGroupItem value="italic" aria-label="Italic">
<ItalicIcon />
</ToggleGroupItem>
<ToggleGroupItem value="underline" aria-label="Underline">
<UnderlineIcon />
</ToggleGroupItem>
</ToggleGroup>Anatomy
<ToggleGroup type="single" defaultValue="left">
<ToggleGroupItem value="left" aria-label="Align left">
{/* icon */}
</ToggleGroupItem>
<ToggleGroupItem value="center" aria-label="Align center">
{/* icon */}
</ToggleGroupItem>
</ToggleGroup>Examples
Single
Set type="single" to bind the bar to one value at a time — e.g. a text-alignment selector.
● LIVE
import { AlignCenterIcon, AlignLeftIcon, AlignRightIcon } from "lucide-react";import { ToggleGroup, ToggleGroupItem } from "@/registry/okibi/ui/toggle-group";export default function ToggleGroupSingleDemo() { return ( <ToggleGroup type="single" defaultValue="left"> <ToggleGroupItem value="left" aria-label="Align left"> <AlignLeftIcon /> </ToggleGroupItem> <ToggleGroupItem value="center" aria-label="Align center"> <AlignCenterIcon /> </ToggleGroupItem> <ToggleGroupItem value="right" aria-label="Align right"> <AlignRightIcon /> </ToggleGroupItem> </ToggleGroup> );}Outline & sizes
The outline variant across the sm/md/lg size scale.
● LIVE
import { AlignCenterIcon, AlignLeftIcon, AlignRightIcon } from "lucide-react";import { ToggleGroup, ToggleGroupItem } from "@/registry/okibi/ui/toggle-group";export default function ToggleGroupOutlineDemo() { return ( <div className="flex flex-col items-start gap-4"> <ToggleGroup type="single" variant="outline" defaultValue="left"> <ToggleGroupItem value="left" aria-label="Align left"> <AlignLeftIcon /> </ToggleGroupItem> <ToggleGroupItem value="center" aria-label="Align center"> <AlignCenterIcon /> </ToggleGroupItem> <ToggleGroupItem value="right" aria-label="Align right"> <AlignRightIcon /> </ToggleGroupItem> </ToggleGroup> <ToggleGroup type="single" variant="outline" size="sm" defaultValue="left"> <ToggleGroupItem value="left" aria-label="Align left"> <AlignLeftIcon /> </ToggleGroupItem> <ToggleGroupItem value="center" aria-label="Align center"> <AlignCenterIcon /> </ToggleGroupItem> <ToggleGroupItem value="right" aria-label="Align right"> <AlignRightIcon /> </ToggleGroupItem> </ToggleGroup> <ToggleGroup type="single" variant="outline" size="lg" defaultValue="left"> <ToggleGroupItem value="left" aria-label="Align left"> <AlignLeftIcon /> </ToggleGroupItem> <ToggleGroupItem value="center" aria-label="Align center"> <AlignCenterIcon /> </ToggleGroupItem> <ToggleGroupItem value="right" aria-label="Align right"> <AlignRightIcon /> </ToggleGroupItem> </ToggleGroup> </div> );}API Reference
ToggleGroup
| Prop | Type | Default | Description |
|---|---|---|---|
type | "single" | "multiple" | – | One-of-many or independent toggles. |
variant | "default" | "outline" | "default" | Shared with every item. |
size | "sm" | "md" | "lg" | "md" | Shared with every item. |
value | string | string[] | – | Controlled value (string for single, array for multiple). |
onValueChange | (value) => void | – | Fires when the selection changes. |
Accessibility
- Radix manages roving focus across items — one Tab stop enters the bar, then arrow keys move between toggles.
- Each item is a real toggle exposing its pressed state;
type='single'enforces one active value,type='multiple'toggles items independently. - Icon-only items carry no text, so each
ToggleGroupItemneeds its ownaria-label. - Items set
focus:z-10so the 2px signal focus ring sits above the hairline keylines instead of being clipped. - The group shares
variant/sizewith its items through context; an item can override either by passing its own prop.
Guidelines
Do
- Set
variant/sizeonce onToggleGroupand let context flow to the items. - Use
type='single'for one-of-many selectors (like alignment) andtype='multiple'for independent formatting toggles. - Give every icon-only
ToggleGroupItemanaria-label.
Don't
- Don't add the
outlinevariant per item inside the strong-bordered bar — it double-borders against the shell; setoutlineon the group instead. - Don't pack unrelated actions into one bar; a toggle group is a segmented control for one related set of states.