React hooks for implementing accessible button components with comprehensive keyboard, mouse, and touch interaction handling.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Extends button behavior with selection state management for toggle buttons that maintain on/off states. The useToggleButton hook builds on useButton to provide accessible toggle functionality with proper ARIA pressed states.
Creates accessible toggle button behavior with selection state management.
/**
* Provides the behavior and accessibility implementation for a toggle button component.
* ToggleButtons allow users to toggle a selection on or off, for example switching between two states or modes.
*/
function useToggleButton(props: AriaToggleButtonOptions<'button'>, state: ToggleState, ref: RefObject<HTMLButtonElement | null>): ToggleButtonAria<ButtonHTMLAttributes<HTMLButtonElement>>;
function useToggleButton(props: AriaToggleButtonOptions<'a'>, state: ToggleState, ref: RefObject<HTMLAnchorElement | null>): ToggleButtonAria<AnchorHTMLAttributes<HTMLAnchorElement>>;
function useToggleButton(props: AriaToggleButtonOptions<'div'>, state: ToggleState, ref: RefObject<HTMLDivElement | null>): ToggleButtonAria<HTMLAttributes<HTMLDivElement>>;
function useToggleButton(props: AriaToggleButtonOptions<'input'>, state: ToggleState, ref: RefObject<HTMLInputElement | null>): ToggleButtonAria<InputHTMLAttributes<HTMLInputElement>>;
function useToggleButton(props: AriaToggleButtonOptions<'span'>, state: ToggleState, ref: RefObject<HTMLSpanElement | null>): ToggleButtonAria<HTMLAttributes<HTMLSpanElement>>;
function useToggleButton(props: AriaToggleButtonOptions<ElementType>, state: ToggleState, ref: RefObject<Element | null>): ToggleButtonAria<DOMAttributes>;Usage Examples:
import { useToggleButton } from "@react-aria/button";
import { useToggleState } from "@react-stately/toggle";
import { useRef, useState } from "react";
// Basic toggle button
function ToggleButton(props) {
let ref = useRef<HTMLButtonElement | null>(null);
let state = useToggleState(props);
let { buttonProps, isPressed, isSelected, isDisabled } = useToggleButton(props, state, ref);
return (
<button
{...buttonProps}
style={{
background: isPressed
? (isSelected ? 'darkgreen' : 'gray')
: (isSelected ? 'green' : 'lightgray'),
color: isSelected ? 'white' : 'black',
padding: 10,
border: 'none'
}}
ref={ref}>
{props.children}
</button>
);
}
// Controlled toggle button
function ControlledToggleButton() {
let [isSelected, setSelected] = useState(false);
return (
<ToggleButton
isSelected={isSelected}
onChange={setSelected}
aria-label="Toggle favorite">
★
</ToggleButton>
);
}
// Custom element toggle button
function CustomToggleButton(props) {
let ref = useRef<HTMLDivElement | null>(null);
let state = useToggleState(props);
let { buttonProps, isSelected, isPressed } = useToggleButton({
...props,
elementType: 'div'
}, state, ref);
return (
<div
{...buttonProps}
style={{
background: isSelected ? '#007acc' : '#f0f0f0',
color: isSelected ? 'white' : 'black',
padding: '8px 16px',
border: isPressed ? '2px solid #005a9e' : '2px solid transparent',
borderRadius: 4,
cursor: 'pointer',
userSelect: 'none'
}}
ref={ref}>
{props.children}
</div>
);
}Configuration options for toggle button behavior.
interface AriaToggleButtonOptions<E extends ElementType> extends Omit<AriaToggleButtonProps<E>, 'children'> {
/** The HTML element or React element used to render the button. @default 'button' */
elementType?: E | JSXElementConstructor<any>;
/** Whether the button is disabled. */
isDisabled?: boolean;
/** Whether the element should be selected (controlled). */
isSelected?: boolean;
/** Whether the element should be selected (uncontrolled). */
defaultSelected?: boolean;
/** Handler that is called when the element's selection state changes. */
onChange?: (isSelected: boolean) => void;
/** Handler that is called when the press is released over the target. */
onPress?: (e: PressEvent) => void;
/** Handler that is called when a press interaction starts. */
onPressStart?: (e: PressEvent) => void;
/** Handler that is called when a press interaction ends. */
onPressEnd?: (e: PressEvent) => void;
/** Handler that is called when the press state changes. */
onPressChange?: (isPressed: boolean) => void;
/** Handler that is called when a press is released over the target. */
onPressUp?: (e: PressEvent) => void;
/** Whether to prevent focus from moving to the button when pressing it. */
preventFocusOnPress?: boolean;
/** Standard onClick handler (called in addition to onPress). */
onClick?: (e: MouseEvent) => void;
}Return value from the useToggleButton hook containing props and state.
interface ToggleButtonAria<T> extends ButtonAria<T> {
/** Whether the button is selected. */
isSelected: boolean;
/** Whether the button is disabled. */
isDisabled: boolean;
}State management interface for toggle buttons (from @react-stately/toggle).
interface ToggleState {
/** Whether the toggle is selected. */
isSelected: boolean;
/** Sets whether the toggle is selected. */
setSelected(isSelected: boolean): void;
/** Toggles the selection state. */
toggle(): void;
}Toggle buttons require state management via the useToggleState hook from @react-stately/toggle:
import { useToggleState } from "@react-stately/toggle";
// Uncontrolled (default selection)
let state = useToggleState({ defaultSelected: false });
// Controlled
let state = useToggleState({
isSelected: isSelected,
onChange: setSelected
});Like useButton, useToggleButton supports multiple HTML element types:
elementType: 'button')aria-pressed attribute for screen readerselementType: 'a')elementType: 'div' | 'span')elementType: 'input')aria-pressed attribute managementThe toggle button automatically manages:
useToggleButton internally uses useButton and extends it with:
All useButton functionality (press states, element types, accessibility) is preserved and enhanced with toggle capabilities.
Install with Tessl CLI
npx tessl i tessl/npm-react-aria--button