ToggleGroup
FormSingle or multiple pressed selections in a compact segmented group.
Responsive viewport — resize the window to explore. Direction and density apply only to this preview. Theme applies to the docs too.
"use client";
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
import {
AlignStartHorizontal,
AlignCenterHorizontal,
AlignEndHorizontal,
Bold,
Italic,
Underline,
} from "lucide-react";
import { useState } from "react";
export default function ToggleGroupExample() {
const [alignment, setAlignment] = useState("start");
return (
<div className="space-y-5">
<div className="space-y-2">
<p className="text-sm font-medium">Single selection</p>
<ToggleGroup
type="single"
value={alignment}
onValueChange={(value) => {
if (value) setAlignment(value);
}}
aria-label="Text alignment"
>
<ToggleGroupItem value="start" aria-label="Align start">
<AlignStartHorizontal aria-hidden />
</ToggleGroupItem>
<ToggleGroupItem value="center" aria-label="Align center">
<AlignCenterHorizontal aria-hidden />
</ToggleGroupItem>
<ToggleGroupItem value="end" aria-label="Align end">
<AlignEndHorizontal aria-hidden />
</ToggleGroupItem>
</ToggleGroup>
</div>
<div className="space-y-2">
<p className="text-sm font-medium">Multiple selection</p>
<ToggleGroup
type="multiple"
defaultValue={["bold"]}
aria-label="Text formatting"
>
<ToggleGroupItem value="bold" aria-label="Bold">
<Bold aria-hidden />
</ToggleGroupItem>
<ToggleGroupItem value="italic" aria-label="Italic">
<Italic aria-hidden />
</ToggleGroupItem>
<ToggleGroupItem value="underline" aria-label="Underline">
<Underline aria-hidden />
</ToggleGroupItem>
</ToggleGroup>
</div>
<p role="status" className="text-xs text-muted-foreground">
Alignment: {alignment}
</p>
</div>
);
}Usage
Compose ToggleGroupItem children with unique values. Choose type=single or type=multiple. Arrow keys move focus; Space or Enter toggles the focused item. Single selection can clear itself; ignore an empty callback value when your application requires exactly one selection.
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";Shipped as editable source in src/components/ui/toggle-group.tsx in both apps. Copy the implementation below when moving it to another project. Copy the complete example from Source code for all imports and state. Add "use client" when using hooks or event handlers. See installation and theming for project setup.
Component source
The complete implementation is included in both apps. It uses the React, Radix, Lucide, and shared-core dependencies already installed in Apex. Keep your project’s semantic theme tokens when reusing it.
src/components/ui/toggle-group.tsx
"use client";
import { useDocumentDirection } from "@/lib/use-document-direction";
import type { ComponentProps } from "react";
import { cn } from "@dashboardpack/core/lib/utils";
import { ToggleGroup as Primitive } from "radix-ui";
export function ToggleGroup({
className,
...props
}: ComponentProps<typeof Primitive.Root>) {
const dir = useDocumentDirection();
return (
<Primitive.Root
dir={dir}
className={cn(
"inline-flex flex-wrap items-center gap-1 rounded-lg border p-1",
className,
)}
{...props}
/>
);
}
export function ToggleGroupItem({
className,
...props
}: ComponentProps<typeof Primitive.Item>) {
return (
<Primitive.Item
className={cn(
"inline-flex h-8 min-w-8 items-center justify-center gap-2 rounded-md px-3 text-sm font-medium outline-none hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring data-[state=on]:bg-secondary data-[state=on]:text-secondary-foreground disabled:pointer-events-none disabled:opacity-50 [&_svg]:size-4",
className,
)}
{...props}
/>
);
}Required helper: src/lib/use-document-direction.ts
Copy this helper alongside the component. It keeps keyboard navigation aligned with the page direction and supports live RTL changes.
"use client";
import { useSyncExternalStore } from "react";
function subscribe(notify: () => void) {
const observer = new MutationObserver(notify);
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ["dir"],
});
return () => observer.disconnect();
}
function snapshot(): "ltr" | "rtl" {
return document.documentElement.dir === "rtl" ? "rtl" : "ltr";
}
/** Keep headless controls aligned with the document, including live RTL changes. */
export function useDocumentDirection() {
return useSyncExternalStore(subscribe, snapshot, () => "ltr" as const);
}API reference
Common props and defaults. Slot-specific props are named below; native elements also accept their standard HTML attributes. The component TypeScript definitions are the complete type reference.
| Prop | Type / Default | Description |
|---|---|---|
| type | single | multipleDefault: required | Controls the selected value shape. |
| value / defaultValue | string | string[]Default: — | Controlled or initial selection. |
| onValueChange | mode-specific callbackDefault: — | Receives selected values. |
| orientation | horizontal | verticalDefault: — | Keyboard navigation direction. |
| disabled | booleanDefault: false | Disable the group or an item. |