Switch

A toggle switch component built on Radix UI for binary on/off states with accessible keyboard support.

Usage

Basic Switch

import { Switch } from "@prisma/eclipse";

export function BasicSwitch() {
  return <Switch />;
}

Live Example:

With Label

Use labels to make switches more accessible and user-friendly:

import { Switch } from "@prisma/eclipse";

export function SwitchWithLabel() {
  return (
    <div className="flex items-center gap-2">
      <Switch id="airplane-mode" />
      <label 
        htmlFor="airplane-mode" 
        className="text-sm font-medium leading-none cursor-pointer"
      >
        Airplane Mode
      </label>
    </div>
  );
}

Live Example:

With Field Component

Use the Field component for proper form structure with labels and descriptions:

import { Switch, Field, FieldLabel, FieldDescription } from "@prisma/eclipse";

export function SwitchWithField() {
  return (
    <Field orientation="horizontal" className="justify-between">
      <div className="flex flex-col gap-1">
        <FieldLabel htmlFor="notifications">Push Notifications</FieldLabel>
        <FieldDescription>Receive push notifications on your device.</FieldDescription>
      </div>
      <Switch id="notifications" />
    </Field>
  );
}

Live Example:

Receive push notifications on your device.

Controlled Switch

Control the switch state with React state:

import { Switch } from "@prisma/eclipse";
import { useState } from "react";

export function ControlledSwitch() {
  const [enabled, setEnabled] = useState(false);

  return (
    <div className="space-y-4">
      <div className="flex items-center gap-2">
        <Switch 
          id="controlled" 
          checked={enabled}
          onCheckedChange={setEnabled}
        />
        <label htmlFor="controlled" className="text-sm font-medium">
          Feature enabled
        </label>
      </div>
      <p className="text-sm text-foreground-neutral-weak">
        Status: {enabled ? "On" : "Off"}
      </p>
    </div>
  );
}

Live Example:

Status: Off

Disabled State

import { Switch } from "@prisma/eclipse";

export function DisabledSwitch() {
  return (
    <div className="space-y-4">
      <div className="flex items-center gap-2">
        <Switch id="disabled-off" disabled />
        <label 
          htmlFor="disabled-off" 
          className="text-sm font-medium opacity-70 cursor-not-allowed"
        >
          Disabled (off)
        </label>
      </div>
      <div className="flex items-center gap-2">
        <Switch id="disabled-on" disabled checked />
        <label 
          htmlFor="disabled-on" 
          className="text-sm font-medium opacity-70 cursor-not-allowed"
        >
          Disabled (on)
        </label>
      </div>
    </div>
  );
}

Live Example:

Settings Panel Example

Create a settings panel with multiple switches:

import { Switch, Field, FieldLabel, FieldDescription } from "@prisma/eclipse";

export function SettingsPanel() {
  return (
    <div className="space-y-6 max-w-md">
      <h3 className="text-lg font-semibold">Notification Settings</h3>
      
      <Field orientation="horizontal" className="justify-between">
        <div className="flex flex-col gap-1">
          <FieldLabel htmlFor="email-notifications">Email Notifications</FieldLabel>
          <FieldDescription>Receive notifications via email.</FieldDescription>
        </div>
        <Switch id="email-notifications" defaultChecked />
      </Field>

      <Field orientation="horizontal" className="justify-between">
        <div className="flex flex-col gap-1">
          <FieldLabel htmlFor="push-notifications">Push Notifications</FieldLabel>
          <FieldDescription>Receive push notifications on your devices.</FieldDescription>
        </div>
        <Switch id="push-notifications" />
      </Field>

      <Field orientation="horizontal" className="justify-between">
        <div className="flex flex-col gap-1">
          <FieldLabel htmlFor="sms-notifications">SMS Notifications</FieldLabel>
          <FieldDescription>Receive notifications via text message.</FieldDescription>
        </div>
        <Switch id="sms-notifications" />
      </Field>
    </div>
  );
}

Live Example:

Notification Settings

Receive notifications via email.

Receive push notifications on your devices.

Receive notifications via text message.

Form Integration

import { Switch } from "@prisma/eclipse";
import { useState } from "react";

export function SwitchForm() {
  const [settings, setSettings] = useState({
    darkMode: false,
    notifications: true,
    analytics: false,
  });

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    console.log("Settings:", settings);
  };

  return (
    <form onSubmit={handleSubmit} className="space-y-6 max-w-md">
      <h3 className="text-lg font-semibold">App Settings</h3>
      
      <div className="flex items-center justify-between">
        <label htmlFor="dark-mode" className="text-sm font-medium">
          Dark Mode
        </label>
        <Switch
          id="dark-mode"
          checked={settings.darkMode}
          onCheckedChange={(checked) => 
            setSettings({ ...settings, darkMode: checked })
          }
        />
      </div>

      <div className="flex items-center justify-between">
        <label htmlFor="notifications-form" className="text-sm font-medium">
          Enable Notifications
        </label>
        <Switch
          id="notifications-form"
          checked={settings.notifications}
          onCheckedChange={(checked) => 
            setSettings({ ...settings, notifications: checked })
          }
        />
      </div>

      <div className="flex items-center justify-between">
        <label htmlFor="analytics-form" className="text-sm font-medium">
          Analytics
        </label>
        <Switch
          id="analytics-form"
          checked={settings.analytics}
          onCheckedChange={(checked) => 
            setSettings({ ...settings, analytics: checked })
          }
        />
      </div>

      <button 
        type="submit"
        className="px-4 py-2 bg-primary text-primary-foreground rounded-md"
      >
        Save Settings
      </button>
    </form>
  );
}

Live Example:

App Settings

Component Props

Switch

The Switch component extends all Radix UI Switch props:

  • checked - Controlled checked state (boolean, optional)
  • defaultChecked - Default checked state for uncontrolled usage (boolean, optional)
  • onCheckedChange - Callback when checked state changes ((checked: boolean) => void, optional)
  • disabled - Whether the switch is disabled (boolean, default: false)
  • required - Whether the switch is required (boolean, default: false)
  • name - Name for form submission (string, optional)
  • value - Value for form submission (string, default: "on")
  • id - HTML id attribute (string, optional)
  • className - Additional CSS classes (string, optional)
  • All standard Radix UI Switch Root props

Features

  • ✅ Built on Radix UI for robust accessibility
  • ✅ Keyboard navigation support (Space/Enter to toggle)
  • ✅ Controlled and uncontrolled modes
  • ✅ Disabled state
  • ✅ Smooth animations and transitions
  • ✅ Focus visible indicators with ring
  • ✅ Works with forms
  • ✅ Screen reader support
  • ✅ Customizable with className
  • ✅ Fully typed with TypeScript
  • ✅ Dark mode support

Best Practices

  • Always pair switches with labels for better UX and accessibility
  • Use htmlFor on labels to match switch id for clickable labels
  • Use switches for immediate state changes (like enabling/disabling features)
  • Use checkboxes for form submissions that require confirmation
  • Position labels to the left of switches in settings panels
  • Use Field components for forms with descriptions
  • Provide clear, concise label text that describes the "on" state
  • Use disabled state sparingly and only when necessary
  • Consider showing immediate feedback when state changes
  • Test keyboard navigation (Tab, Space, Enter)
  • Ensure labels clearly communicate what the switch controls

Common Use Cases

The Switch component is perfect for:

  • Settings toggles - Enable/disable application features
  • Feature flags - Turn features on or off
  • Notification preferences - Control notification channels
  • Privacy settings - Manage privacy and data sharing
  • Theme switching - Toggle dark/light mode
  • Accessibility options - Enable assistive features
  • Permission controls - Grant or revoke permissions
  • Status toggles - Active/inactive states
  • Display options - Show/hide UI elements
  • Auto-save - Enable automatic saving

Switch vs Checkbox

Use Switch when:

  • The change takes effect immediately
  • Toggling a feature on/off
  • Representing a binary state (enabled/disabled)
  • In settings or preferences panels

Use Checkbox when:

  • Part of a form that requires submission
  • Selecting multiple items from a list
  • Accepting terms or agreements
  • The change requires confirmation

Accessibility

The Switch component follows accessibility best practices:

  • Uses Radix UI's accessible switch primitive
  • Proper ARIA attributes (role="switch", aria-checked)
  • Keyboard accessible (Space/Enter to toggle, Tab to navigate)
  • Focus visible indicators with ring
  • Screen reader support with proper labels
  • Disabled state prevents interaction
  • Works with form validation
  • Supports required attribute
  • Proper focus management
  • High contrast in both checked and unchecked states
  • Smooth animations respect prefers-reduced-motion

Keyboard Shortcuts

  • Space or Enter - Toggle switch on/off
  • Tab - Move focus to next element
  • Shift + Tab - Move focus to previous element

Styling

The Switch component uses Eclipse design tokens:

  • Unchecked background: bg-input
  • Checked background: bg-primary
  • Thumb: bg-background (unchecked), bg-primary-foreground (checked in dark mode)
  • Focus ring: ring-ring/50 with 3px width
  • Border: Transparent with shadow
  • Border radius: Fully rounded (rounded-full)
  • Shadow: shadow-xs

Customize by passing className:

<Switch className="data-[state=checked]:bg-green-500" />

Integration with Other Components

Switch works well with:

  • Field - Form structure with labels and descriptions
  • FieldLabel - Accessible labels
  • FieldDescription - Helper text
  • Forms - Settings and preferences
  • Cards - Feature toggles within cards
  • Lists - Settings lists
  • Dialogs - Preference dialogs

Form Integration

The Switch component works seamlessly with forms:

<form onSubmit={handleSubmit}>
  <Switch name="notifications" value="enabled" defaultChecked />
  <button type="submit">Submit</button>
</form>

When checked, the form data will include: notifications=enabled

Animation

The Switch component includes smooth animations:

  • Thumb slides horizontally when toggled
  • Background color transitions smoothly
  • Focus ring fades in/out
  • All animations use CSS transitions
  • Respects user's motion preferences

Dark Mode

The Switch component automatically adapts to dark mode:

  • Unchecked state uses bg-input/80 in dark mode
  • Thumb uses bg-foreground when unchecked in dark mode
  • Checked state remains consistent across themes
  • All colors use design tokens for automatic theme adaptation

On this page