Aura Design System

Switch

A control that allows the user to toggle between on and off states.

Preview

Loading...
import { useState } from "react";
import { Switch } from "@/components/ui/Switch";

export function SwitchDemo() {
  return {
  const [checked, setChecked] = useState(false);

  return (
    <div className="flex items-center gap-2">
      <Switch id="default" checked={checked} onCheckedChange={setChecked} />
      <label
        htmlFor="default"
        className="text-sm font-medium leading-none cursor-pointer"
      >
        Airplane Mode
      </label>
    </div>
  );
};

Installation

Make sure that namespace is set in your component.json file. Namespace docs: Learn more about namespaces

pnpm dlx shadcn@latest add @aura/switch

Manual

Install the following dependencies:

pnpm install radix-ui

Copy and paste the class names utility into your utils/class-names.ts file.

import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

Copy and paste the Switch component into your components/ui/Switch.tsx file.

"use client";
/**
 * @description A control that allows the user to toggle between on and off states.
 */
import * as React from "react";
import { Switch as SwitchPrimitive } from "radix-ui";

import { cn } from "@/utils/class-names";

function Switch({
  className,
  ...props
}: React.ComponentProps<typeof SwitchPrimitive.Root>) {
  return (
    <SwitchPrimitive.Root
      data-slot="switch"
      className={cn(
        "relative h-1.5 w-2.5 cursor-pointer rounded-full outline-none bg-accent-4 data-[state=checked]:bg-accent-10 border border-gray-a6 hover:bg-accent-5",
        className
      )}
      {...props}
    >
      <SwitchThumb />
    </SwitchPrimitive.Root>
  );
}

function SwitchThumb({
  className,
  ...props
}: React.ComponentProps<typeof SwitchPrimitive.Thumb>) {
  return (
    <SwitchPrimitive.Thumb
      data-slot="switch-thumb"
      className={cn(
        "block size-1 translate-x-[3.5px] rounded-full bg-gray-1 shadow-md transition-transform duration-100 will-change-transform data-[state=checked]:translate-x-[15.5px]",
        className
      )}
      {...props}
    />
  );
}

export { Switch, SwitchThumb };

Usage

Checked

export const Checked = () => (
  <div className="flex items-center gap-2">
    <Switch id="checked" defaultChecked />
    <label
      htmlFor="checked"
      className="text-sm font-medium leading-none cursor-pointer"
    >
      Bluetooth
    </label>
  </div>
)

Unchecked

export const Unchecked = () => (
  <div className="flex items-center gap-2">
    <Switch id="unchecked" />
    <label
      htmlFor="unchecked"
      className="text-sm font-medium leading-none cursor-pointer"
    >
      Wi-Fi
    </label>
  </div>
)

Disabled

export const Disabled = () => (
  <div className="flex flex-col gap-2">
    <div className="flex items-center gap-2">
      <Switch id="disabled-unchecked" disabled />
      <label
        htmlFor="disabled-unchecked"
        className="text-sm font-medium leading-none text-gray-a8 cursor-not-allowed"
      >
        Disabled unchecked
      </label>
    </div>
    <div className="flex items-center gap-2">
      <Switch id="disabled-checked" disabled defaultChecked />
      <label
        htmlFor="disabled-checked"
        className="text-sm font-medium leading-none text-gray-a8 cursor-not-allowed"
      >
        Disabled checked
      </label>
    </div>
  </div>
)

WithDescription

export const WithDescription = () => {
  const [checked, setChecked] = useState(false);

  return (
    <div className="flex items-start gap-2">
      <Switch
        id="with-description"
        checked={checked}
        onCheckedChange={setChecked}
        className="mt-0.5"
      />
      <div className="flex flex-col gap-1">
        <label
          htmlFor="with-description"
          className="text-sm font-medium leading-none cursor-pointer"
        >
          Marketing emails
        </label>
        <p className="text-sm text-gray-a11 m-0">
          Receive emails about new products, features, and more.
        </p>
      </div>
    </div>
  );
};

SwitchGroup

export const SwitchGroup = () => {
  const [settings, setSettings] = useState({
    notifications: true,
    marketing: false,
    security: true,
    updates: false,
  });

  const items = [
    {
      id: "notifications",
      label: "Push Notifications",
      description: "Receive push notifications on your device",
    },
    {
      id: "marketing",
      label: "Marketing Emails",
      description: "Receive emails about new products and features",
    },
    {
      id: "security",
      label: "Two-Factor Authentication",
      description: "Enable two-factor authentication for your account",
    },
    {
      id: "updates",
      label: "Automatic Updates",
      description: "Automatically install software updates",
    },
  ];

  const handleCheckedChange = (
    key: keyof typeof settings,
    checked: boolean
  ) => {
    setSettings((prev) => ({ ...prev, [key]: checked }));
  };

  return (
    <div className="flex flex-col gap-4">
      <div className="text-sm font-semibold">System Settings:</div>
      {items.map((item) => (
        <div key={item.id} className="flex items-start gap-2">
          <Switch
            id={item.id}
            checked={settings[item.id as keyof typeof settings]}
            onCheckedChange={(checked) =>
              handleCheckedChange(item.id as keyof typeof settings, checked)
            }
            className="mt-0.5"
          />
          <div className="flex flex-col gap-1">
            <label
              htmlFor={item.id}
              className="text-sm font-medium leading-none cursor-pointer"
            >
              {item.label}
            </label>
            <p className="text-sm text-gray-a11 m-0">{item.description}</p>
          </div>
        </div>
      ))}
      <div className="mt-2 text-sm text-gray-a11">
        Active settings: {Object.values(settings).filter(Boolean).length}
      </div>
    </div>
  );
};