dotUI
dotUI
beta
  1. Components
  2. Buttons
  3. Toggle Button

Toggle Button

Toggle Buttons allow users to toggle a selection on or off.

<ToggleButton><PinIcon /></ToggleButton>

Installation

Install the following dependencies:

npm install react-aria-components

Copy and paste the following code into your project.

"use client";

import * as React from "react";
import {
  composeRenderProps,
  ToggleButton as AriaToggleButton,
  type ToggleButtonProps as AriaToggleButtonProps,
} from "react-aria-components";
import { tv, type VariantProps } from "tailwind-variants";
import { focusRing } from "@/lib/utils/styles";

const toggleButtonStyles = tv({
  extend: focusRing,
  base: "inline-flex items-center justify-center gap-2 rounded-md leading-normal text-sm font-medium ring-offset-background transition-colors disabled:cursor-default disabled:bg-bg-disabled disabled:text-fg-disabled",
  variants: {
    variant: {
      quiet:
        "bg-transparent hover:bg-bg-inverse/10 pressed:bg-bg-inverse/20 text-fg selected:bg-bg-primary selected:text-fg-onPrimary selected:hover:bg-bg-primary-hover selected:pressed:bg-bg-primary-active",
      outline:
        "border border-border-field bg-transparent hover:bg-bg-inverse/10 pressed:bg-bg-inverse/20 pressed:border-transparent text-fg selected:bg-bg-primary selected:border-transparent selected:text-fg-onPrimary selected:hover:bg-bg-primary-hover selected:pressed:bg-bg-primary-active",
      accent:
        "border border-border-field bg-transparent hover:bg-bg-inverse/10 pressed:bg-bg-inverse/20 pressed:border-transparent text-fg selected:bg-bg-accent selected:border-transparent selected:hover:bg-bg-accent-hover selected:pressed:bg-bg-accent-active selected:text-fg-onAccent",
    },
    size: {
      sm: "size-8 [&_svg]:size-4",
      md: "size-9 [&_svg]:size-4",
      lg: "size-10 [&_svg]:size-5",
    },
    shape: {
      rectangle: "",
      square: "",
      circle: "rounded-full",
    },
  },
  compoundVariants: [
    {
      size: "sm",
      shape: "rectangle",
      className: "px-3 w-auto",
    },
    {
      size: "md",
      shape: "rectangle",
      className: "px-4 w-auto",
    },
    {
      size: "lg",
      shape: "rectangle",
      className: "px-5 w-auto",
    },
  ],
  defaultVariants: {
    variant: "quiet",
    size: "md",
    shape: "square",
  },
});

interface ToggleButtonProps
  extends Omit<AriaToggleButtonProps, "className">,
    VariantProps<typeof toggleButtonStyles> {
  className?: string;
  prefix?: React.ReactNode;
  suffix?: React.ReactNode;
}

const ToggleButton = React.forwardRef(
  (localProps: ToggleButtonProps, ref: React.ForwardedRef<HTMLButtonElement>) => {
    const contextProps = useToggleButtonContext();
    const props = { ...contextProps, ...localProps };
    const { className, variant, size, shape, prefix, suffix, ...restProps } = props;
    return (
      <AriaToggleButton
        ref={ref}
        {...restProps}
        className={toggleButtonStyles({
          variant,
          size,
          shape,
          className,
        })}
      >
        {composeRenderProps(props.children, (children) => (
          <>
            {prefix}
            {typeof children === "string" ? <span className="truncate">{children}</span> : children}
            {suffix}
          </>
        ))}
      </AriaToggleButton>
    );
  }
);
ToggleButton.displayName = "ToggleButton";

type ToggleButtonContextValue = VariantProps<typeof toggleButtonStyles>;
const ToggleButtonContext = React.createContext<ToggleButtonContextValue>({});
const useToggleButtonContext = () => {
  return React.useContext(ToggleButtonContext);
};

export type { ToggleButtonProps };
export { ToggleButton, toggleButtonStyles, ToggleButtonContext };

Update the import paths to match your project setup.

Usage

Toggle button allow users to toggle a selection on or off, for example, switching between two states or modes.
They can have a label, an icon, or both. An icon is provided by passing an icon component as child to the ToggleButton or by using the prefix and suffix props. A visible label can be provided by passing a string as a child.

Options

Variants

Use the variant prop to control the visual style of the toggle button. The default size is "quiet".

<ToggleButton variant="quiet"><PinIcon /></ToggleButton>
<ToggleButton variant="outline"><PinIcon /></ToggleButton>
<ToggleButton variant="accent"><PinIcon /></ToggleButton>

Sizes

Use the size prop to control the size of the toggle button.
The default size is "md".

<ToggleButton size="sm"><PinIcon /></ToggleButton>
<ToggleButton size="md"><PinIcon /></ToggleButton>
<ToggleButton size="lg"><PinIcon /></ToggleButton>

Shapes

Use the shape prop to control the shape of the toggle button. The default shape is "rectangle".
Icon-only toggle buttons should include an aria-label.

<ToggleButton shape="square"><PinIcon /></ToggleButton>
<ToggleButton shape="circular"><PinIcon /></ToggleButton>
<ToggleButton prefix={<PinIcon />} shape="rectangle">Pin</ToggleButton>

Disabled

Use the isDisabled prop to disable the toggle button.

<ToggleButton isDisabled><PinIcon /></ToggleButton>

Uncontrolled

Use the defaultSelected to set the initial state of the toggle button.

<ToggleButton defaultSelected><PinIcon /></ToggleButton>

Controlled

Use the isSelected and onChange props to control the state of the toggle button.

const [isSelected, setSelected] = React.useState(true);
return (
  <ToggleButton isSelected={isSelected} onChange={setSelected}>
    <PinIcon className="rotate-45" />
  </ToggleButton>
);

API Reference

PropTypeDefaultDescription
variant
"quiet" | "outline" | "accent"
"quiet"
The visual style of the button.
size
"sm" | "md" | "lg"
"md"
The size of the button.
shape
"rectangle" | "square" | "circle"
"square"
The visual shape of the button.
prefix
React.ReactNode
-
A visual to display before the button text.
suffix
React.ReactNode
-
A visual to display after the button text.
isSelected
boolean
-
Whether the element should be selected (controlled).
defaultSelected
boolean
-
Whether the element should be selected (uncontrolled).
isDisabled
boolean
-
Whether the button is disabled.
autoFocus
boolean
-
Whether the element should receive focus on render.
type
'button' | 'submit' | 'reset'
-
The behavior of the button when used in an HTML form.
children
ReactNode | (values: ToggleButtonRenderProps & {defaultChildren: ReactNode | undefined}) => ReactNode
-
The children of the component. A function may be provided to alter the children based on component state.
className
string
-
The CSS className for the element.
style
CSSProperties | (values: ToggleButtonRenderProps & {defaultStyle: CSSProperties}) => CSSProperties
-
The inline style for the element. A function may be provided to compute the style based on component state.
EventTypeDescription
onChange
(isSelected: boolean) => void
Handler that is called when the element's selection state changes.
onPress
(e: PressEvent) => void
Handler that is called when the press is released over the target.
onPressStart
(e: PressEvent) => void
Handler that is called when a press interaction starts.
onPressEnd
(e: PressEvent) => void
Handler that is called when a press interaction ends, either over the target or when the pointer leaves the target.
onPressChange
(isPressed: boolean) => void
Handler that is called when the press state changes.
onPressUp
(e: PressEvent) => void
Handler that is called when a press is released over the target, regardless of whether it started on the target or not.
onFocus
(e: FocusEvent<Target>) => void
Handler that is called when the element receives focus.
onBlur
(e: FocusEvent<Target>) => void
Handler that is called when the element loses focus.
onFocusChange
(isFocused: boolean) => void
Handler that is called when the element's focus status changes.
onKeyDown
(e: KeyboardEvent) => void
Handler that is called when a key is pressed.
onKeyUp
(e: KeyboardEvent) => void
Handler that is called when a key is released.
onHoverStart
(e: HoverEvent) => void
Handler that is called when a hover interaction starts.
onHoverEnd
(e: HoverEvent) => void
Handler that is called when a hover interaction ends.
onHoverChange
(isHovering: boolean) => void
Handler that is called when the hover state changes.
Data attributeDescription
[data-selected]
Whether the button is currently selected.
[data-hovered]
Whether the button is currently hovered with a mouse.
[data-pressed]
Whether the button is currently in a pressed state.
[data-focused]
Whether the button is focused, either via a mouse or keyboard.
[data-focus-visible]
Whether the button is keyboard focused.
[data-disabled]
Whether the button is disabled.

Accessibility

Keyboard interactions

KeyDescription
Space
Activates/deactivates the toggle.
Enter
Activates/deactivates the toggle.

Built by mehdibha. The source code is available on GitHub.