Toggle Group React component for Tailwind CSS v4
v1.0.0Added in 1.0.0
A React toggle group: a row of toggles where one or several stay pressed, for formatting, alignment, view switches and filters.
Click or tap an item and it stays pressed. In single mode the highlight slides over to the new item on a smooth spring and the old one lets go; in multiple mode each item pops its own highlight in and out. Every item dips a little while your finger is down. The group is one tab stop: arrow keys move along it (wrapping around unless loop is off), Home and End jump to the ends, Space or Enter presses. Icon-only items name themselves in a tooltip on hover or keyboard focus, with the shortcut hint next to the name; on a phone a long press shows it without pressing the item. On touch the medium size is 44px tall and the small one keeps a 44px hit area.
Toggle Group is a Pro React component in the React button components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add toggle-group, together with Tooltip, Motion presets, Tones, useControllableState and UIProvider and the npm packages @radix-ui/react-toggle-group and motion.
Add to a project
Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup
npx wingo-ui@latest add toggle-groupToggle Group is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans
Usage
Import ToggleGroup from @/components/ui/toggle-group.
import { AlignCenter, AlignLeft, AlignRight, Bold, Italic, Underline } from "lucide-react"
import { ToggleGroup, ToggleGroupSeparator } from "@/components/ui/toggle-group"
export function EditorToolbar({ align, onAlignChange }: { align: string; onAlignChange: (value: string) => void }) {
return (
<div role="toolbar" aria-label="Editor" className="flex items-center gap-1">
<ToggleGroup
type="multiple"
iconOnly
defaultValue={["bold"]}
aria-label="Formatting"
options={[
{ value: "bold", label: "Bold", icon: <Bold />, shortcut: "mod+b" },
{ value: "italic", label: "Italic", icon: <Italic />, shortcut: "mod+i" },
{ value: "underline", label: "Underline", icon: <Underline />, shortcut: "mod+u" },
]}
/>
<ToggleGroupSeparator />
<ToggleGroup
iconOnly
value={align}
onValueChange={onAlignChange}
aria-label="Alignment"
options={[
{ value: "left", label: "Left", icon: <AlignLeft /> },
{ value: "center", label: "Center", icon: <AlignCenter /> },
{ value: "right", label: "Right", icon: <AlignRight /> },
]}
/>
</div>
)
}Props
typedefault"single""single" | "multiple"single: one item stays pressed and the value is a string (an alignment, a view). multiple: any number stay pressed and the value is a string[] (bold, italic, filters).
optionsToggleGroupOption[]{ value: string; label: ReactNode; icon?: ReactNode; disabled?: boolean; tooltip?: ReactNode; shortcut?: string }. The shortcut is a hint shown in the tooltip and set as aria-keyshortcuts; bind it yourself with useShortcut. Values that disappear from the list are dropped from the value.
valuestring | string[]Controlled value, typed by type: a string for single (empty for none), a string[] for multiple.
defaultValuestring | string[]Starting value while uncontrolled: "" or [] when omitted.
onValueChange(value: string | string[]) => voidFires with the new value, typed by type: a string for single ("" when cleared), a string[] for multiple.
deselectabledefaultfalsebooleansingle only: clicking the pressed item clears the value. Off by default, since an alignment or a view always has one.
variantdefault"ghost""ghost" | "outline" | "soft"ghost: transparent items for toolbars, pressed ones get the selected tint. outline: items joined into one bordered control with hairlines between them. soft: a tinted track where the pressed item becomes a solid fill.
sizedefault"md""sm" | "md" | "lg"Height 32 / 40 / 48px with matching padding, type and icon size; icon-only items are square. On touch screens md grows to 44px and sm keeps a 44px hit area.
radiusdefault"md""none" | "sm" | "md" | "lg" | "full"Corner shape, scaled with the size like the button's. full turns the group into a pill.
tonedefault"neutral""neutral" | "brand" | "success" | "warning" | "danger" | "info"Color of the pressed items. neutral is the gray selected tint (a black or white fill on soft); the other tones tint the highlight and color the text.
colordefault""stringAny CSS color for the pressed items; replaces the tone. Leave empty to use the tone.
orientationdefault"horizontal""horizontal" | "vertical"The direction of the row and of the arrow keys. Vertical items align their labels to the start.
iconOnlydefaultfalsebooleanShows only the icons in square items. Each label becomes the item's aria-label and its tooltip (with the shortcut hint).
fullWidthdefaultfalsebooleanItems share the width of the container, the usual layout for a view switch on a phone.
attacheddefaulttruebooleanoutline only: items share their borders as one control. Off: they stand apart as separate chips with a gap; add flex-wrap through className to let them wrap.
loopdefaulttruebooleanArrow keys wrap from the last item to the first and back. Off: they stop at the ends.
disableddefaultfalsebooleanDims the whole group and blocks it. A single option can be disabled through its disabled field.
aria-labeldefault""stringThe group's name for screen readers, like Alignment. Needed when no visible label sits next to it.
classNamesPartial<Record<"item" | "highlight" | "icon" | "label", string>>Extra classes for every item, the pressed highlight, the icon wrapper and the label wrapper.
classNamestringExtra classes for the group itself, merged last.
Parts
ToggleGroupRoot
componentThe group without options, for custom layouts: the same props as ToggleGroup minus options, with ToggleGroupItem and ToggleGroupSeparator children. Props: ToggleGroupRootProps<T>.
ToggleGroupItem
componentOne item; children is the label. Takes every button prop too. Props: { value: string; icon?: ReactNode; tooltip?: ReactNode; shortcut?: string; disabled?: boolean; children?: ReactNode }.
ToggleGroupSeparator
componentA hairline between clusters inside a group or between two groups in a toolbar; it runs across the group's direction unless you set orientation. Props: { orientation?: "horizontal" | "vertical" }.
ToggleGroupOption
typeOne item of options: value, label, icon, disabled, tooltip (hover, focus and long press) and shortcut (a mod+k hint shown in the tooltip and set as aria-keyshortcuts; bind it with useShortcut).
ToggleGroupValue
typeThe value type: a string for type="single", a string[] for type="multiple".
Examples
Variants
ghost for toolbars, outline for one bordered control, soft for a track with a solid pressed fill.
View switch
Single, soft and full width, with icons and labels: the highlight slides as the view changes.
Quick filters
type="multiple" with attached={false}: outline chips that wrap, each popping its own highlight in and out.
Vertical tool palette
Parts with a separator; icon-only items show their name and shortcut in a tooltip on the right. With focus in the palette, the letter keys pick a tool.
Sizes and radii
32, 40 and 48px; the outline row runs from square corners to a pill.
Tones
Brand for the billing period, success for the channels that are on.
Files
- components/ui/toggle-group.tsx
FAQ
Is the React Toggle Group component free?
The preview, props and docs are free; copying the source needs a Pro plan. See plans.
How do I install Toggle Group?
Run npx wingo-ui@latest add toggle-group. It also adds the Wingo UI items it builds on (Tooltip, Motion presets, Tones, useControllableState and UIProvider) and the npm packages @radix-ui/react-toggle-group and motion. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/toggle-group.
How does Toggle Group behave on phones?
Icon-only items name themselves in a tooltip on hover or keyboard focus, with the shortcut hint next to the name; on a phone a long press shows it without pressing the item.
See also
Used in
More React button components
Articles using this component
- React Segmented Control: iOS Thumb, Drag and When to Use It
Mobile UI in React8 min read