Toggle

A two-state button that can be either on or off.

Demo

Dependencies

clsxtailwind-merge

Props

This is a base primitive. It accepts standard HTML attributes plus className for styling via cn().

Source

"use client";

import { useState, useCallback } from "react";
import { cn } from "@/lib/utils";

export interface ToggleProps {
  pressed?: boolean;
  defaultPressed?: boolean;
  onPressedChange?: (pressed: boolean) => void;
  disabled?: boolean;
  children?: React.ReactNode;
  className?: string;
}

export function Toggle({
  pressed: controlledPressed,
  defaultPressed = false,
  onPressedChange,
  disabled,
  children,
  className,
}: ToggleProps) {
  const [internalPressed, setInternalPressed] = useState(defaultPressed);
  const isControlled = controlledPressed !== undefined;
  const pressed = isControlled ? controlledPressed : internalPressed;

  const handleClick = useCallback(() => {
    if (disabled) return;
    const next = !pressed;
    if (!isControlled) setInternalPressed(next);
    onPressedChange?.(next);
  }, [pressed, disabled, isControlled, onPressedChange]);

  return (
    <button
      type="button"
      aria-pressed={pressed}
      disabled={disabled}
      onClick={handleClick}
      className={cn(
        "inline-flex items-center justify-center rounded-md px-3 py-2 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-gray-400 disabled:pointer-events-none disabled:opacity-50",
        pressed
          ? "bg-gray-200 text-gray-900"
          : "bg-transparent text-gray-500 hover:bg-gray-100 hover:text-gray-700",
        className,
      )}
    >
      {children}
    </button>
  );
}

CLI

npx genui add toggle