Was this helpful?
Preview
Design guidance
Use Toggle for a persistent on/off choice. Use Button for an action and Switch for a setting. Give familiar icons an accessible name without repeating it as visible text. Keep text for symbols whose meaning needs explanation.
Supply a matching pressedIcon when the icon family has a filled version. Toggle uses its existing pressed state to swap the pair. Other ghost toggles use an inner selected surface around their icon and text. Stroke icons use a 1px line weight. Counter values and feedback submission stay in caller state.
Installation
Install
The examples use optional icons from @phosphor-icons/react. Install that package to copy the regular/fill examples.
Basic usage
Structures
Existing children, native attributes, refs and Base UI render composition remain available. The optional counter is included in the button's accessible description; caller aria-describedby values are preserved.
Examples
Icon with text
Keep a label when the symbol is less familiar.
Icon
The supplied outline heart becomes filled when pressed.
Outline
Use an individual boundary when the action needs it.
Sizes
The xs, sm, base and lg controls start at 24, 28, 32 and 40px high. Base 32px matches Button and Input. Stacked counters grow to fit their content and padding. Native coarse-pointer targets remain at least 44px.
Counters
Counter positions below are right, bottom, left and top. The example owns its count and selection; Toggle only displays the supplied value. Zero and formatted strings such as 1.2k remain visible. Top/bottom counters use the same size-based padding as horizontal controls and grow in height. Long counts can grow in width.
Disabled
Disabled controls retain their on or off state.
API reference
| Prop | Type | Default | Description |
|---|---|---|---|
content | "icon" | "text" | "icon-text" | "icon" | Square icon geometry or text padding; children remain caller-owned. |
variant | "ghost" | "outline" | "ghost" | Shared Button appearance with persistent state. No subtle variant. |
size | "xs" | "sm" | "base" | "lg" | "base" | 24, 28, 32 or 40px; inherited from ToggleGroup unless set on the item. |
icon / pressedIcon | ReactNode | โ | Optional icon pair. pressedIcon is shown only while pressed; icon is required for the pair. Arbitrary children are never converted into filled shapes. |
counter | number | string | โ | Optional displayed count, including zero. Does not change selection, increment itself, format a value or submit feedback. |
counterPosition | "left" | "right" | "top" | "bottom" | "right" | Counter position relative to content; inherited from ToggleGroup unless explicitly set. |
pressed / defaultPressed | boolean | false (uncontrolled) | Controlled or initial pressed state; exposed as aria-pressed. |
onPressedChange | (pressed, eventDetails) => void | โ | Existing Base UI change event; cancellation and disabled behavior are preserved. |
render / ref / className / style | Base UI Toggle props | โ | Native button/render composition and state callbacks remain available. |
Behavior and advanced props follow Base UI Toggle.