A two-state button component with full accessibility support and keyboard navigation
npx @tessl/cli install tessl/npm-radix-ui--react-toggle@1.1.0Radix UI React Toggle is a fully accessible two-state button component that can be pressed/unpressed. Built on Radix UI's primitive foundation, it provides comprehensive accessibility features, keyboard navigation, and flexible state management patterns for React applications.
npm install @radix-ui/react-togglenpm install react@^16.8.0)import { Toggle, Root } from "@radix-ui/react-toggle";
import type { ToggleProps } from "@radix-ui/react-toggle";For CommonJS:
const { Toggle, Root } = require("@radix-ui/react-toggle");import { Toggle } from "@radix-ui/react-toggle";
// Uncontrolled toggle with default state
function BasicToggle() {
return (
<Toggle defaultPressed={false} onPressedChange={(pressed) => console.log(pressed)}>
Toggle Me
</Toggle>
);
}
// Controlled toggle
function ControlledToggle() {
const [isPressed, setIsPressed] = useState(false);
return (
<Toggle pressed={isPressed} onPressedChange={setIsPressed}>
{isPressed ? "On" : "Off"}
</Toggle>
);
}The Toggle component provides a clean, accessible interface for two-state interactions:
asChild prop for flexible element compositionThe main toggle component that renders as a button with press/unpress functionality.
/**
* Toggle component - A two-state button that can be pressed/unpressed
*/
const Toggle: React.ForwardRefExoticComponent<
ToggleProps & React.RefAttributes<HTMLButtonElement>
>;
/**
* Root component - Alias for Toggle following Radix UI naming conventions
*/
const Root: React.ForwardRefExoticComponent<
ToggleProps & React.RefAttributes<HTMLButtonElement>
>;Usage Examples:
// Basic uncontrolled usage
<Toggle defaultPressed onPressedChange={(pressed) => console.log(pressed)}>
Like
</Toggle>
// Controlled usage
<Toggle pressed={isPressed} onPressedChange={setIsPressed}>
{isPressed ? "Liked" : "Like"}
</Toggle>
// With custom styling via data attributes
<Toggle
className="toggle-button"
data-testid="like-button"
>
<HeartIcon />
</Toggle>
// Using asChild for custom element
<Toggle asChild>
<button className="custom-toggle-button">
Custom Toggle
</button>
</Toggle>
// Using Root alias
<Toggle.Root pressed={isPressed}>
Toggle Content
</Toggle.Root>Complete props interface extending standard HTML button attributes.
interface ToggleProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
/**
* The controlled state of the toggle
*/
pressed?: boolean;
/**
* The state of the toggle when initially rendered
* Use when you do not need to control the state
* @defaultValue false
*/
defaultPressed?: boolean;
/**
* Callback that fires when the state of the toggle changes
*/
onPressedChange?: (pressed: boolean) => void;
/**
* When true, merges props into the child element instead of rendering a button
* Enables the slot pattern for custom element composition
*/
asChild?: boolean;
/**
* All standard HTML button attributes are supported:
* - Event handlers: onClick, onMouseDown, onMouseUp, onDoubleClick, etc.
* - Form attributes: type, disabled, form, name, value, etc.
* - Accessibility: aria-*, role, tabIndex, etc.
* - Styling: className, style, data-*, etc.
* - Standard attributes: id, title, lang, dir, etc.
*/
}When you need to manage the toggle state externally:
function ControlledExample() {
const [isPressed, setIsPressed] = useState(false);
// External state updates
useEffect(() => {
if (someCondition) {
setIsPressed(true);
}
}, [someCondition]);
return (
<Toggle
pressed={isPressed}
onPressedChange={setIsPressed}
>
{isPressed ? "Active" : "Inactive"}
</Toggle>
);
}When the toggle manages its own state internally:
function UncontrolledExample() {
return (
<Toggle
defaultPressed={false}
onPressedChange={(pressed) => {
// React to state changes without controlling the state
console.log("Toggle is now:", pressed ? "pressed" : "unpressed");
}}
>
Toggle Me
</Toggle>
);
}The Toggle component provides comprehensive accessibility features:
// When pressed=false or unpressed
<button aria-pressed="false" data-state="off">...</button>
// When pressed=true or pressed
<button aria-pressed="true" data-state="on">...</button>
// When disabled
<button disabled data-disabled="">...</button>aria-pressed attributeThe component automatically applies data attributes for CSS styling:
/* Target different states */
.toggle-button[data-state="on"] {
background-color: blue;
color: white;
}
.toggle-button[data-state="off"] {
background-color: gray;
color: black;
}
.toggle-button[data-disabled] {
opacity: 0.5;
cursor: not-allowed;
}Use the asChild prop to merge toggle functionality into custom elements:
function CustomToggle() {
return (
<Toggle asChild pressed={isPressed} onPressedChange={setIsPressed}>
<div className="custom-toggle-div" role="button" tabIndex={0}>
<Icon name={isPressed ? "heart-filled" : "heart-outline"} />
<span>{isPressed ? "Liked" : "Like"}</span>
</div>
</Toggle>
);
}Toggle properly composes event handlers when extending functionality:
function EnhancedToggle() {
const handleClick = (event) => {
console.log("Custom click handler");
// Toggle's internal handler will still run unless preventDefault() is called
};
return (
<Toggle
onClick={handleClick}
onPressedChange={(pressed) => console.log("State changed:", pressed)}
>
Enhanced Toggle
</Toggle>
);
}Toggle works seamlessly with forms:
function FormToggle() {
return (
<form>
<Toggle
name="notifications"
value="enabled"
defaultPressed
onPressedChange={(pressed) => {
// Update form state or trigger validation
}}
>
Enable Notifications
</Toggle>
</form>
);
}/**
* Complete props interface for Toggle component
*/
interface ToggleProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
pressed?: boolean;
defaultPressed?: boolean;
onPressedChange?: (pressed: boolean) => void;
asChild?: boolean;
}