Switch

A toggle switch component for boolean values. Supports controlled and uncontrolled modes with multiple sizes.

Installation

Terminal
ReactTailwind
TSX
npx @uiblox/cli add switch

Visual Variations

Toggle between three different visual styles: default (rounded), ios (larger), and square (rectangular).

Off

Click to toggle

Enabled

Active toggle

Locked

Non-interactive

Basic Usage

Off
Copy & paste ready
import { Switch } from "@/components/ui";

const [checked, setChecked] = useState(false);

<Switch checked={checked} onCheckedChange={setChecked} />
<span>{checked ? "On" : "Off"}</span>

Sizes

Copy & paste ready
<Switch size="sm" defaultChecked />
<Switch size="md" defaultChecked />
<Switch size="lg" defaultChecked />

States

Unchecked
Checked
Disabled
Disabled Checked
ReactTailwind CSSCopy & paste ready
<Switch /> {/* Unchecked */}
<Switch defaultChecked /> {/* Checked */}
<Switch disabled /> {/* Disabled */}
<Switch disabled defaultChecked /> {/* Disabled Checked */}

With Labels

Dark Mode

Enable dark theme

ReactTailwind CSSCopy & paste ready
<div className="flex items-center justify-between w-64 p-4 border rounded-lg">
  <div>
    <p className="font-medium">Dark Mode</p>
    <p className="text-sm text-gray-500">Enable dark theme</p>
  </div>
  <Switch />
</div>

Props

PropTypeDefaultDescription
checkedboolean-Controlled checked state
defaultCheckedbooleanfalseDefault checked state for uncontrolled usage
onCheckedChange(checked: boolean) => void-Callback when checked state changes
size"sm" | "md" | "lg""md"The size of the switch
disabledbooleanfalseWhether the switch is disabled

Source Code

Copy this code into src/components/ui/switch.tsx:

switch.tsx
ReactTailwind
TSX
"use client";

import * as React from "react";
import { cn } from "@/lib/utils";

interface SwitchProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "onChange"> {
  checked?: boolean;
  defaultChecked?: boolean;
  onCheckedChange?: (checked: boolean) => void;
  size?: "sm" | "md" | "lg";
}

const Switch = React.forwardRef<HTMLButtonElement, SwitchProps>(
  ({ className, checked, defaultChecked = false, onCheckedChange, size = "md", disabled, ...props }, ref) => {
    const [internalChecked, setInternalChecked] = React.useState(defaultChecked);
    const isChecked = checked ?? internalChecked;

    const handleClick = () => {
      if (disabled) return;
      const newChecked = !isChecked;
      if (checked === undefined) {
        setInternalChecked(newChecked);
      }
      onCheckedChange?.(newChecked);
    };

    const sizeClasses = {
      sm: "h-4 w-7",
      md: "h-5 w-9",
      lg: "h-6 w-11",
    };

    const thumbSizeClasses = {
      sm: "h-3 w-3",
      md: "h-4 w-4",
      lg: "h-5 w-5",
    };

    const thumbTranslateClasses = {
      sm: "translate-x-3",
      md: "translate-x-4",
      lg: "translate-x-5",
    };

    return (
      <button
        ref={ref}
        type="button"
        role="switch"
        aria-checked={isChecked}
        disabled={disabled}
        onClick={handleClick}
        className={cn(
          "inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors",
          "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-2",
          "disabled:cursor-not-allowed disabled:opacity-50",
          isChecked ? "bg-primary-600" : "bg-gray-200 dark:bg-gray-700",
          sizeClasses[size],
          className
        )}
        {...props}
      >
        <span
          className={cn(
            "pointer-events-none block rounded-full bg-white shadow-lg transition-transform",
            thumbSizeClasses[size],
            isChecked ? thumbTranslateClasses[size] : "translate-x-0.5"
          )}
        />
      </button>
    );
  }
);
Switch.displayName = "Switch";

export { Switch };