GitHub

Switch

Binary toggle for on/off settings with optional label and helper text.

01 Live Demo

Enable analytics

Analytics is active (enabled)

Email alerts

Receive weekly summary emails. (disabled)

02 Code Snippet

src/ui/Switch.tsx
import { Switch } from "@/ui/Switch";

<Switch.Root
  checked={enabled}
  onChange={setEnabled}
  label="Enable analytics"
  description="Track usage data for product insights."
/>

03 Copy-Paste (Single File)

Switch.tsx
import { useState } from "react";

type SwitchProps = {
  checked?: boolean;
  defaultChecked?: boolean;
  onChange?: (checked: boolean) => void;
  label?: string;
  description?: string;
};

function SwitchRoot({ checked, defaultChecked = false, onChange, label, description }: SwitchProps) {
  const [internal, setInternal] = useState(defaultChecked);
  const isControlled = checked !== undefined;
  const isOn = isControlled ? checked : internal;

  const toggle = () => {
    const next = !isOn;
    if (!isControlled) setInternal(next);
    onChange?.(next);
  };

  return (
    <div className="inline-flex items-start gap-3">
      <button
        type="button"
        role="switch"
        aria-checked={isOn}
        onClick={toggle}
        className={"relative h-6 w-11 rounded-full p-0.5 transition-all " + (isOn ? "bg-primary" : "bg-slate-300 dark:bg-slate-700")}
      >
        <span className={"block h-5 w-5 rounded-full bg-white transition-transform " + (isOn ? "translate-x-5" : "translate-x-0")} />
      </button>
      {(label || description) && (
        <div>
          {label && <p className="text-sm font-medium text-slate-900 dark:text-white">{label}</p>}
          {description && <p className="text-xs text-slate-500 dark:text-slate-400">{description}</p>}
        </div>
      )}
    </div>
  );
}

type SwitchCompound = typeof SwitchRoot & { Root: typeof SwitchRoot };
export const Switch = Object.assign(SwitchRoot, { Root: SwitchRoot }) as SwitchCompound;
react-principles