implement
Toggle Group

Toggle Group

A joined row of toggles that share one value.

import { signal } from "@implementjs/core";
import { BoldIcon, ItalicIcon, UnderlineIcon } from "@implementjs/lucide";
import { ToggleGroup, ToggleGroupItem } from "@/lib/components/ui/toggle-group";

export default function ToggleGroupDemo() {
	const value = signal<string[]>(["bold"]);

	return ToggleGroup(
		{ type: "multiple", value, "aria-label": "Text formatting" },
		ToggleGroupItem(
			{ value: "bold", variant: "outline", "aria-label": "Toggle bold" },
			BoldIcon({ "aria-hidden": true }),
		),
		ToggleGroupItem(
			{ value: "italic", variant: "outline", "aria-label": "Toggle italic" },
			ItalicIcon({ "aria-hidden": true }),
		),
		ToggleGroupItem(
			{ value: "underline", variant: "outline", "aria-label": "Toggle underline" },
			UnderlineIcon({ "aria-hidden": true }),
		),
	);
}

Installation

npx jsrepo add @implementjs/ui/toggle-group

jsrepo pulls toggle along with it.

Usage

The group is styled as a joined row: the items square off against each other and only the ends stay rounded, so an outlined group reads as one control rather than three buttons.

Items take the same variant and size as a standalone toggle — set them per item, since that is where the styles land.

import { signal } from "@implementjs/core";
import { ToggleGroup, ToggleGroupItem } from "@/lib/components/ui/toggle-group";

const value = signal<string[]>(["bold"]);

ToggleGroup(
	{ type: "multiple", value, "aria-label": "Text formatting" },
	ToggleGroupItem(
		{ value: "bold", variant: "outline", "aria-label": "Toggle bold" },
		BoldIcon({ "aria-hidden": true }),
	),
);

Single or multiple

type defaults to "single" — one item at a time, value a Signal<string | null>. "multiple" lets several stay pressed and makes value a Signal<string[]>.

Outline items

In the outline variant the items drop their left border except on the first, so adjacent borders do not double up into a thick line. That rule keys off data-variant, which the item sets from its own variant prop.

API Reference

Every prop the styling does not consume is forwarded to the Toggle Group primitive, so the tables below are the whole surface — the behavior props and the styling ones together.

ToggleGroup

The root. Owns which items are pressed and the arrow-key focus movement. Sets role="group". Styled as a joined row: items square off against each other and only the ends are rounded. Renders a Div; extra props are forwarded onto it.

PropTypeDefaultDescription
type"single" | "multiple""single"Whether pressing an item releases the others, or several can stay on.
valueSignal<string | null> | Signal<string[]>The pressed value(s). string | null when type is "single", string[] when "multiple". Pass a signal to control it from outside.
disabledSignal<boolean> | booleanfalseDisables every item in the group.
loopbooleantrueWhether arrow keys wrap from the last item back to the first.
orientation"horizontal" | "vertical""horizontal"Which arrow keys move focus, and the data-orientation attributes.
Data attributeValue
[data-toggle-group-root]Present
[data-orientation]"horizontal" | "vertical"
[data-disabled]Present when disabled

ToggleGroupItem

One toggle. role="radio" with aria-checked in a single group, aria-pressed in a multiple group. Renders a Button; extra props are forwarded onto it.

PropTypeDefaultDescription
variant"default" | "outline""default"Transparent by default; outline adds a border. Also set as data-variant.
size"default" | "sm" | "lg""default"The toggle's height and padding scale. Also set as data-size.
value*stringIdentifies the item. Must be unique within the group.
disabledSignal<boolean> | booleanfalsePrevents pressing the item. Sets disabled and data-disabled.
Data attributeValue
[data-toggle-group-item]Present
[data-state]"on" | "off"
[data-value]The item's value
[data-orientation]"horizontal" | "vertical"
[data-disabled]Present when disabled